]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/smb/smb.rs
app-layer: include decoder events in app-layer tx data
[people/ms/suricata.git] / rust / src / smb / smb.rs
CommitLineData
d41aecce 1/* Copyright (C) 2017-2020 Open Information Security Foundation
75d7c9d6
VJ
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/* TODO
19 * - check all parsers for calls on non-SUCCESS status
20 */
21
22/* GAP processing:
23 * - if post-gap we've seen a succesful tx req/res: we consider "re-sync'd"
24 */
25
26// written by Victor Julien
3f6624bf 27
75d7c9d6 28use std;
75d7c9d6 29use std::str;
b9f10ba2 30use std::ffi::{self, CString};
75d7c9d6 31
75d7c9d6
VJ
32use std::collections::HashMap;
33
13b73997
PC
34use nom;
35
42e5065a 36use crate::core::*;
42e5065a 37use crate::applayer;
6420df84 38use crate::applayer::*;
e5c948df 39use crate::conf::*;
5226ba1c 40use crate::filecontainer::*;
b9f10ba2 41use crate::applayer::{AppLayerResult, AppLayerTxData, AppLayerEvent};
42e5065a
JI
42
43use crate::smb::nbss_records::*;
44use crate::smb::smb1_records::*;
45use crate::smb::smb2_records::*;
46
47use crate::smb::smb1::*;
48use crate::smb::smb2::*;
49use crate::smb::smb3::*;
50use crate::smb::dcerpc::*;
51use crate::smb::session::*;
52use crate::smb::events::*;
53use crate::smb::files::*;
54use crate::smb::smb2_ioctl::*;
75d7c9d6 55
4d6b6b5d
SB
56pub const MIN_REC_SIZE: u16 = 32 + 4; // SMB hdr + nbss hdr
57pub const SMB_CONFIG_DEFAULT_STREAM_DEPTH: u32 = 0;
58
6420df84
SB
59static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN;
60
75d7c9d6
VJ
61pub static mut SURICATA_SMB_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
62
63#[no_mangle]
64pub extern "C" fn rs_smb_init(context: &'static mut SuricataFileContext)
65{
66 unsafe {
67 SURICATA_SMB_FILE_CONFIG = Some(context);
68 }
69}
70
75d7c9d6
VJ
71pub const SMB_NTSTATUS_SUCCESS: u32 = 0;
72pub const SMB_NTSTATUS_PENDING: u32 = 0x00000103;
73pub const SMB_NTSTATUS_BUFFER_OVERFLOW: u32 = 0x80000005;
74pub const SMB_NTSTATUS_NO_MORE_FILES: u32 = 0x80000006;
75pub const SMB_NTSTATUS_NO_MORE_ENTRIES: u32 = 0x8000001a;
76pub const SMB_NTSTATUS_INVALID_HANDLE: u32 = 0xc0000008;
77pub const SMB_NTSTATUS_INVALID_PARAMETER: u32 = 0xc000000d;
78pub const SMB_NTSTATUS_NO_SUCH_DEVICE: u32 = 0xc000000e;
79pub const SMB_NTSTATUS_NO_SUCH_FILE: u32 = 0xc000000f;
80pub const SMB_NTSTATUS_INVALID_DEVICE_REQUEST: u32 = 0xc0000010;
81pub const SMB_NTSTATUS_END_OF_FILE: u32 = 0xc0000011;
82pub const SMB_NTSTATUS_MORE_PROCESSING_REQUIRED: u32 = 0xc0000016;
83pub const SMB_NTSTATUS_ACCESS_DENIED: u32 = 0xc0000022;
84pub const SMB_NTSTATUS_OBJECT_NAME_INVALID: u32 = 0xc0000033;
85pub const SMB_NTSTATUS_OBJECT_NAME_NOT_FOUND: u32 = 0xc0000034;
86pub const SMB_NTSTATUS_OBJECT_NAME_COLLISION: u32 = 0xc0000035;
87pub const SMB_NTSTATUS_OBJECT_PATH_NOT_FOUND: u32 = 0xc000003a;
88pub const SMB_NTSTATUS_SHARING_VIOLATION: u32 = 0xc0000043;
89pub const SMB_NTSTATUS_LOCK_CONFLICT: u32 = 0xc0000054;
90pub const SMB_NTSTATUS_LOCK_NOT_GRANTED: u32 = 0xc0000055;
91pub const SMB_NTSTATUS_PRIVILEGE_NOT_HELD: u32 = 0xc0000061;
92pub const SMB_NTSTATUS_LOGON_FAILURE: u32 = 0xc000006d;
93pub const SMB_NTSTATUS_PIPE_DISCONNECTED: u32 = 0xc00000b0;
94pub const SMB_NTSTATUS_FILE_IS_A_DIRECTORY: u32 = 0xc00000ba;
95pub const SMB_NTSTATUS_NOT_SUPPORTED: u32 = 0xc00000bb;
96pub const SMB_NTSTATUS_BAD_NETWORK_NAME: u32 = 0xc00000cc;
7ceb6713 97pub const SMB_NTSTATUS_REQUEST_NOT_ACCEPTED: u32 = 0xc00000d0;
75d7c9d6
VJ
98pub const SMB_NTSTATUS_OPLOCK_NOT_GRANTED: u32 = 0xc00000e2;
99pub const SMB_NTSTATUS_CANCELLED: u32 = 0xc0000120;
100pub const SMB_NTSTATUS_FILE_CLOSED: u32 = 0xc0000128;
101pub const SMB_NTSTATUS_FS_DRIVER_REQUIRED: u32 = 0xc000019c;
102pub const SMB_NTSTATUS_INSUFF_SERVER_RESOURCES: u32 = 0xc0000205;
103pub const SMB_NTSTATUS_NOT_FOUND: u32 = 0xc0000225;
104pub const SMB_NTSTATUS_PIPE_BROKEN: u32 = 0xc000014b;
105pub const SMB_NTSTATUS_TRUSTED_RELATIONSHIP_FAILURE: u32 = 0xc000018d;
106pub const SMB_NTSTATUS_NOT_A_REPARSE_POINT: u32 = 0xc0000275;
107pub const SMB_NTSTATUS_NETWORK_SESSION_EXPIRED: u32 = 0xc000035c;
108
109pub fn smb_ntstatus_string(c: u32) -> String {
110 match c {
111 SMB_NTSTATUS_SUCCESS => "STATUS_SUCCESS",
112 SMB_NTSTATUS_BUFFER_OVERFLOW => "STATUS_BUFFER_OVERFLOW",
113 SMB_NTSTATUS_PENDING => "STATUS_PENDING",
114 SMB_NTSTATUS_NO_MORE_FILES => "STATUS_NO_MORE_FILES",
115 SMB_NTSTATUS_NO_MORE_ENTRIES => "STATUS_NO_MORE_ENTRIES",
116 SMB_NTSTATUS_INVALID_HANDLE => "STATUS_INVALID_HANDLE",
117 SMB_NTSTATUS_INVALID_PARAMETER => "STATUS_INVALID_PARAMETER",
118 SMB_NTSTATUS_NO_SUCH_DEVICE => "STATUS_NO_SUCH_DEVICE",
119 SMB_NTSTATUS_NO_SUCH_FILE => "STATUS_NO_SUCH_FILE",
120 SMB_NTSTATUS_INVALID_DEVICE_REQUEST => "STATUS_INVALID_DEVICE_REQUEST",
121 SMB_NTSTATUS_END_OF_FILE => "STATUS_END_OF_FILE",
122 SMB_NTSTATUS_MORE_PROCESSING_REQUIRED => "STATUS_MORE_PROCESSING_REQUIRED",
123 SMB_NTSTATUS_ACCESS_DENIED => "STATUS_ACCESS_DENIED",
124 SMB_NTSTATUS_OBJECT_NAME_INVALID => "STATUS_OBJECT_NAME_INVALID",
125 SMB_NTSTATUS_OBJECT_NAME_NOT_FOUND => "STATUS_OBJECT_NAME_NOT_FOUND",
126 SMB_NTSTATUS_OBJECT_NAME_COLLISION => "STATUS_OBJECT_NAME_COLLISION",
127 SMB_NTSTATUS_OBJECT_PATH_NOT_FOUND => "STATUS_OBJECT_PATH_NOT_FOUND",
128 SMB_NTSTATUS_SHARING_VIOLATION => "STATUS_SHARING_VIOLATION",
129 SMB_NTSTATUS_LOCK_CONFLICT => "STATUS_LOCK_CONFLICT",
130 SMB_NTSTATUS_LOCK_NOT_GRANTED => "STATUS_LOCK_NOT_GRANTED",
131 SMB_NTSTATUS_PRIVILEGE_NOT_HELD => "STATUS_PRIVILEGE_NOT_HELD",
132 SMB_NTSTATUS_LOGON_FAILURE => "STATUS_LOGON_FAILURE",
133 SMB_NTSTATUS_PIPE_DISCONNECTED => "STATUS_PIPE_DISCONNECTED",
134 SMB_NTSTATUS_FILE_IS_A_DIRECTORY => "STATUS_FILE_IS_A_DIRECTORY",
135 SMB_NTSTATUS_NOT_SUPPORTED => "STATUS_NOT_SUPPORTED",
136 SMB_NTSTATUS_BAD_NETWORK_NAME => "STATUS_BAD_NETWORK_NAME",
7ceb6713 137 SMB_NTSTATUS_REQUEST_NOT_ACCEPTED => "STATUS_REQUEST_NOT_ACCEPTED",
75d7c9d6
VJ
138 SMB_NTSTATUS_OPLOCK_NOT_GRANTED => "STATUS_OPLOCK_NOT_GRANTED",
139 SMB_NTSTATUS_CANCELLED => "STATUS_CANCELLED",
140 SMB_NTSTATUS_FILE_CLOSED => "STATUS_FILE_CLOSED",
141 SMB_NTSTATUS_FS_DRIVER_REQUIRED => "STATUS_FS_DRIVER_REQUIRED",
142 SMB_NTSTATUS_INSUFF_SERVER_RESOURCES => "STATUS_INSUFF_SERVER_RESOURCES",
143 SMB_NTSTATUS_NOT_FOUND => "STATUS_NOT_FOUND",
144 SMB_NTSTATUS_PIPE_BROKEN => "STATUS_PIPE_BROKEN",
145 SMB_NTSTATUS_TRUSTED_RELATIONSHIP_FAILURE => "STATUS_TRUSTED_RELATIONSHIP_FAILURE",
146 SMB_NTSTATUS_NOT_A_REPARSE_POINT => "STATUS_NOT_A_REPARSE_POINT",
147 SMB_NTSTATUS_NETWORK_SESSION_EXPIRED => "STATUS_NETWORK_SESSION_EXPIRED",
148 _ => { return (c).to_string(); },
149 }.to_string()
150}
151
816bd022
VJ
152pub const SMB_SRV_ERROR: u16 = 1;
153pub const SMB_SRV_BADPW: u16 = 2;
154pub const SMB_SRV_BADTYPE: u16 = 3;
155pub const SMB_SRV_ACCESS: u16 = 4;
156pub const SMB_SRV_BADUID: u16 = 91;
157
158pub fn smb_srv_error_string(c: u16) -> String {
159 match c {
160 SMB_SRV_ERROR => "SRV_ERROR",
161 SMB_SRV_BADPW => "SRV_BADPW",
162 SMB_SRV_BADTYPE => "SRV_BADTYPE",
163 SMB_SRV_ACCESS => "SRV_ACCESS",
164 SMB_SRV_BADUID => "SRV_BADUID",
165 _ => { return (c).to_string(); },
166 }.to_string()
167}
75d7c9d6
VJ
168
169pub const SMB_DOS_SUCCESS: u16 = 0;
170pub const SMB_DOS_BAD_FUNC: u16 = 1;
171pub const SMB_DOS_BAD_FILE: u16 = 2;
172pub const SMB_DOS_BAD_PATH: u16 = 3;
173pub const SMB_DOS_TOO_MANY_OPEN_FILES: u16 = 4;
174pub const SMB_DOS_ACCESS_DENIED: u16 = 5;
175
176pub fn smb_dos_error_string(c: u16) -> String {
177 match c {
178 SMB_DOS_SUCCESS => "DOS_SUCCESS",
179 SMB_DOS_BAD_FUNC => "DOS_BAD_FUNC",
180 SMB_DOS_BAD_FILE => "DOS_BAD_FILE",
181 SMB_DOS_BAD_PATH => "DOS_BAD_PATH",
182 SMB_DOS_TOO_MANY_OPEN_FILES => "DOS_TOO_MANY_OPEN_FILES",
183 SMB_DOS_ACCESS_DENIED => "DOS_ACCESS_DENIED",
184 _ => { return (c).to_string(); },
185 }.to_string()
186}
187
188pub const NTLMSSP_NEGOTIATE: u32 = 1;
189pub const NTLMSSP_CHALLENGE: u32 = 2;
190pub const NTLMSSP_AUTH: u32 = 3;
191
192pub fn ntlmssp_type_string(c: u32) -> String {
193 match c {
194 NTLMSSP_NEGOTIATE => "NTLMSSP_NEGOTIATE",
195 NTLMSSP_CHALLENGE => "NTLMSSP_CHALLENGE",
196 NTLMSSP_AUTH => "NTLMSSP_AUTH",
197 _ => { return (c).to_string(); },
198 }.to_string()
199}
200
02dccb15 201#[derive(Default, Eq, PartialEq, Debug, Clone)]
75d7c9d6
VJ
202pub struct SMBVerCmdStat {
203 smb_ver: u8,
204 smb1_cmd: u8,
205 smb2_cmd: u16,
206
207 status_set: bool,
208 status_is_dos_error: bool,
816bd022 209 status_error_class: u8,
75d7c9d6
VJ
210 status: u32,
211}
212
213impl SMBVerCmdStat {
02dccb15
JL
214 pub fn new() -> Self {
215 Default::default()
75d7c9d6 216 }
02dccb15
JL
217 pub fn new1(cmd: u8) -> Self {
218 return Self {
75d7c9d6
VJ
219 smb_ver: 1,
220 smb1_cmd: cmd,
02dccb15 221 ..Default::default()
75d7c9d6
VJ
222 }
223 }
02dccb15
JL
224 pub fn new1_with_ntstatus(cmd: u8, status: u32) -> Self {
225 return Self {
75d7c9d6
VJ
226 smb_ver: 1,
227 smb1_cmd: cmd,
75d7c9d6 228 status_set: true,
75d7c9d6 229 status: status,
02dccb15 230 ..Default::default()
75d7c9d6
VJ
231 }
232 }
02dccb15
JL
233 pub fn new2(cmd: u16) -> Self {
234 return Self {
75d7c9d6 235 smb_ver: 2,
75d7c9d6 236 smb2_cmd: cmd,
02dccb15 237 ..Default::default()
75d7c9d6
VJ
238 }
239 }
240
02dccb15
JL
241 pub fn new2_with_ntstatus(cmd: u16, status: u32) -> Self {
242 return Self {
75d7c9d6 243 smb_ver: 2,
75d7c9d6
VJ
244 smb2_cmd: cmd,
245 status_set: true,
75d7c9d6 246 status: status,
02dccb15 247 ..Default::default()
75d7c9d6
VJ
248 }
249 }
250
251 pub fn set_smb1_cmd(&mut self, cmd: u8) -> bool {
252 if self.smb_ver != 0 {
253 return false;
254 }
255 self.smb_ver = 1;
256 self.smb1_cmd = cmd;
257 return true;
258 }
259
260 pub fn set_smb2_cmd(&mut self, cmd: u16) -> bool {
261 if self.smb_ver != 0 {
262 return false;
263 }
264 self.smb_ver = 2;
265 self.smb2_cmd = cmd;
266 return true;
267 }
268
269 pub fn get_version(&self) -> u8 {
270 self.smb_ver
271 }
272
273 pub fn get_smb1_cmd(&self) -> (bool, u8) {
274 if self.smb_ver != 1 {
275 return (false, 0);
276 }
277 return (true, self.smb1_cmd);
278 }
279
280 pub fn get_smb2_cmd(&self) -> (bool, u16) {
281 if self.smb_ver != 2 {
282 return (false, 0);
283 }
284 return (true, self.smb2_cmd);
285 }
286
287 pub fn get_ntstatus(&self) -> (bool, u32) {
288 (self.status_set && !self.status_is_dos_error, self.status)
289 }
290
816bd022
VJ
291 pub fn get_dos_error(&self) -> (bool, u8, u16) {
292 (self.status_set && self.status_is_dos_error, self.status_error_class, self.status as u16)
75d7c9d6
VJ
293 }
294
295 fn set_status(&mut self, status: u32, is_dos_error: bool)
296 {
297 if is_dos_error {
298 self.status_is_dos_error = true;
816bd022 299 self.status_error_class = (status & 0x0000_00ff) as u8;
75d7c9d6
VJ
300 self.status = (status & 0xffff_0000) >> 16;
301 } else {
302 self.status = status;
303 }
304 self.status_set = true;
305 }
306
307 pub fn set_ntstatus(&mut self, status: u32)
308 {
309 self.set_status(status, false)
310 }
311
312 pub fn set_status_dos_error(&mut self, status: u32)
313 {
314 self.set_status(status, true)
315 }
316}
317
0e05ef73
VJ
318/// "The FILETIME structure is a 64-bit value that represents the number of
319/// 100-nanosecond intervals that have elapsed since January 1, 1601,
320/// Coordinated Universal Time (UTC)."
321#[derive(Eq, PartialEq, Debug, Clone)]
322pub struct SMBFiletime {
323 ts: u64,
324}
325
326impl SMBFiletime {
02dccb15
JL
327 pub fn new(raw: u64) -> Self {
328 Self {
0e05ef73
VJ
329 ts: raw,
330 }
331 }
332
333 /// inspired by Bro, convert FILETIME to secs since unix epoch
334 pub fn as_unix(&self) -> u32 {
335 if self.ts > 116_444_736_000_000_000_u64 {
336 let ts = self.ts / 10000000 - 11644473600;
337 ts as u32
338 } else {
339 0
340 }
341 }
342}
343
75d7c9d6
VJ
344#[derive(Debug)]
345pub enum SMBTransactionTypeData {
346 FILE(SMBTransactionFile),
347 TREECONNECT(SMBTransactionTreeConnect),
348 NEGOTIATE(SMBTransactionNegotiate),
349 DCERPC(SMBTransactionDCERPC),
350 CREATE(SMBTransactionCreate),
8bef1208 351 SESSIONSETUP(SMBTransactionSessionSetup),
5c260207 352 IOCTL(SMBTransactionIoctl),
7b61f2c5 353 RENAME(SMBTransactionRename),
2b581cd6
VJ
354 SETFILEPATHINFO(SMBTransactionSetFilePathInfo),
355}
356
357// Used for Trans2 SET_PATH_INFO and SET_FILE_INFO
358#[derive(Debug)]
359pub struct SMBTransactionSetFilePathInfo {
360 pub subcmd: u16,
361 pub loi: u16,
362 pub delete_on_close: bool,
363 pub filename: Vec<u8>,
364 pub fid: Vec<u8>,
365}
366
367impl SMBTransactionSetFilePathInfo {
368 pub fn new(filename: Vec<u8>, fid: Vec<u8>, subcmd: u16, loi: u16, delete_on_close: bool)
02dccb15 369 -> Self
2b581cd6 370 {
02dccb15 371 return Self {
2b581cd6
VJ
372 filename: filename, fid: fid,
373 subcmd: subcmd,
374 loi: loi,
375 delete_on_close: delete_on_close,
376 }
377 }
378}
379
380impl SMBState {
381 pub fn new_setfileinfo_tx(&mut self, filename: Vec<u8>, fid: Vec<u8>,
382 subcmd: u16, loi: u16, delete_on_close: bool)
5ee83230 383 -> &mut SMBTransaction
2b581cd6
VJ
384 {
385 let mut tx = self.new_tx();
386
387 tx.type_data = Some(SMBTransactionTypeData::SETFILEPATHINFO(
388 SMBTransactionSetFilePathInfo::new(
389 filename, fid, subcmd, loi, delete_on_close)));
390 tx.request_done = true;
391 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
392
393 SCLogDebug!("SMB: TX SETFILEPATHINFO created: ID {}", tx.id);
394 self.transactions.push(tx);
395 let tx_ref = self.transactions.last_mut();
396 return tx_ref.unwrap();
397 }
398
399 pub fn new_setpathinfo_tx(&mut self, filename: Vec<u8>,
400 subcmd: u16, loi: u16, delete_on_close: bool)
5ee83230 401 -> &mut SMBTransaction
2b581cd6
VJ
402 {
403 let mut tx = self.new_tx();
404
405 let fid : Vec<u8> = Vec::new();
406 tx.type_data = Some(SMBTransactionTypeData::SETFILEPATHINFO(
407 SMBTransactionSetFilePathInfo::new(filename, fid,
408 subcmd, loi, delete_on_close)));
409 tx.request_done = true;
410 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
411
412 SCLogDebug!("SMB: TX SETFILEPATHINFO created: ID {}", tx.id);
413 self.transactions.push(tx);
414 let tx_ref = self.transactions.last_mut();
415 return tx_ref.unwrap();
416 }
7b61f2c5
VJ
417}
418
419#[derive(Debug)]
420pub struct SMBTransactionRename {
421 pub oldname: Vec<u8>,
422 pub newname: Vec<u8>,
423 pub fuid: Vec<u8>,
424}
425
426impl SMBTransactionRename {
02dccb15
JL
427 pub fn new(fuid: Vec<u8>, oldname: Vec<u8>, newname: Vec<u8>) -> Self {
428 return Self {
7b61f2c5
VJ
429 fuid: fuid, oldname: oldname, newname: newname,
430 }
431 }
432}
433
434impl SMBState {
435 pub fn new_rename_tx(&mut self, fuid: Vec<u8>, oldname: Vec<u8>, newname: Vec<u8>)
5ee83230 436 -> &mut SMBTransaction
7b61f2c5
VJ
437 {
438 let mut tx = self.new_tx();
439
440 tx.type_data = Some(SMBTransactionTypeData::RENAME(
441 SMBTransactionRename::new(fuid, oldname, newname)));
442 tx.request_done = true;
443 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
444
ea1e13cb 445 SCLogDebug!("SMB: TX RENAME created: ID {}", tx.id);
7b61f2c5
VJ
446 self.transactions.push(tx);
447 let tx_ref = self.transactions.last_mut();
448 return tx_ref.unwrap();
449 }
75d7c9d6
VJ
450}
451
02dccb15 452#[derive(Default, Debug)]
75d7c9d6
VJ
453pub struct SMBTransactionCreate {
454 pub disposition: u32,
455 pub delete_on_close: bool,
456 pub directory: bool,
457 pub filename: Vec<u8>,
fb986abe 458 pub guid: Vec<u8>,
0e05ef73
VJ
459
460 pub create_ts: u32,
461 pub last_access_ts: u32,
462 pub last_write_ts: u32,
463 pub last_change_ts: u32,
464
465 pub size: u64,
75d7c9d6
VJ
466}
467
468impl SMBTransactionCreate {
02dccb15
JL
469 pub fn new(filename: Vec<u8>, disp: u32, del: bool, dir: bool) -> Self {
470 return Self {
75d7c9d6
VJ
471 disposition: disp,
472 delete_on_close: del,
473 directory: dir,
474 filename: filename,
02dccb15 475 ..Default::default()
75d7c9d6
VJ
476 }
477 }
478}
479
02dccb15 480#[derive(Default, Debug)]
75d7c9d6
VJ
481pub struct SMBTransactionNegotiate {
482 pub smb_ver: u8,
483 pub dialects: Vec<Vec<u8>>,
484 pub dialects2: Vec<Vec<u8>>,
6d56edc3
VJ
485
486 // SMB1 doesn't have the client GUID
487 pub client_guid: Option<Vec<u8>>,
488 pub server_guid: Vec<u8>,
75d7c9d6
VJ
489}
490
491impl SMBTransactionNegotiate {
02dccb15
JL
492 pub fn new(smb_ver: u8) -> Self {
493 return Self {
75d7c9d6 494 smb_ver: smb_ver,
6d56edc3 495 server_guid: Vec::with_capacity(16),
02dccb15 496 ..Default::default()
75d7c9d6
VJ
497 }
498 }
499}
500
02dccb15 501#[derive(Default, Debug)]
75d7c9d6
VJ
502pub struct SMBTransactionTreeConnect {
503 pub is_pipe: bool,
c56f5e11 504 pub share_type: u8,
75d7c9d6
VJ
505 pub tree_id: u32,
506 pub share_name: Vec<u8>,
fcbeab70
VJ
507
508 /// SMB1 service strings
509 pub req_service: Option<Vec<u8>>,
510 pub res_service: Option<Vec<u8>>,
75d7c9d6
VJ
511}
512
513impl SMBTransactionTreeConnect {
02dccb15
JL
514 pub fn new(share_name: Vec<u8>) -> Self {
515 return Self {
75d7c9d6 516 share_name:share_name,
02dccb15 517 ..Default::default()
75d7c9d6
VJ
518 }
519 }
520}
521
522#[derive(Debug)]
523pub struct SMBTransaction {
524 pub id: u64, /// internal id
525
526 /// version, command and status
527 pub vercmd: SMBVerCmdStat,
528 /// session id, tree id, etc.
529 pub hdr: SMBCommonHdr,
530
531 /// for state tracking. false means this side is in progress, true
532 /// that it's complete.
533 pub request_done: bool,
534 pub response_done: bool,
535
536 /// Command specific data
537 pub type_data: Option<SMBTransactionTypeData>,
538
77a95edd 539 pub tx_data: AppLayerTxData,
75d7c9d6
VJ
540}
541
fcfc9876
JI
542impl Transaction for SMBTransaction {
543 fn id(&self) -> u64 {
544 self.id
545 }
546}
547
75d7c9d6 548impl SMBTransaction {
02dccb15
JL
549 pub fn new() -> Self {
550 return Self {
551 id: 0,
552 vercmd: SMBVerCmdStat::new(),
553 hdr: SMBCommonHdr::init(),
554 request_done: false,
555 response_done: false,
556 type_data: None,
02dccb15 557 tx_data: AppLayerTxData::new(),
75d7c9d6
VJ
558 }
559 }
560
561 pub fn set_status(&mut self, status: u32, is_dos_error: bool)
562 {
563 if is_dos_error {
564 self.vercmd.set_status_dos_error(status);
565 } else {
566 self.vercmd.set_ntstatus(status);
567 }
568 }
569
570 pub fn free(&mut self) {
c9cee7af
VJ
571 debug_validate_bug_on!(self.tx_data.files_opened > 1);
572 debug_validate_bug_on!(self.tx_data.files_logged > 1);
75d7c9d6
VJ
573 }
574}
575
576impl Drop for SMBTransaction {
577 fn drop(&mut self) {
578 self.free();
579 }
580}
581
582#[derive(Hash, Eq, PartialEq, Debug, Clone)]
583pub struct SMBFileGUIDOffset {
584 pub guid: Vec<u8>,
585 pub offset: u64,
586}
587
588impl SMBFileGUIDOffset {
02dccb15
JL
589 pub fn new(guid: Vec<u8>, offset: u64) -> Self {
590 Self {
75d7c9d6
VJ
591 guid:guid,
592 offset:offset,
593 }
594 }
595}
596
597/// type values to make sure we're not mixing things
598/// up in hashmap lookups
599pub const SMBHDR_TYPE_GUID: u32 = 1;
600pub const SMBHDR_TYPE_SHARE: u32 = 2;
601pub const SMBHDR_TYPE_FILENAME: u32 = 3;
602pub const SMBHDR_TYPE_OFFSET: u32 = 4;
603pub const SMBHDR_TYPE_GENERICTX: u32 = 5;
604pub const SMBHDR_TYPE_HEADER: u32 = 6;
605pub const SMBHDR_TYPE_MAX_SIZE: u32 = 7; // max resp size for SMB1_COMMAND_TRANS
1b86d4e1
VJ
606pub const SMBHDR_TYPE_TRANS_FRAG: u32 = 8;
607pub const SMBHDR_TYPE_TREE: u32 = 9;
608pub const SMBHDR_TYPE_DCERPCTX: u32 = 10;
75d7c9d6 609
02dccb15 610#[derive(Default, Hash, Eq, PartialEq, Debug)]
75d7c9d6
VJ
611pub struct SMBCommonHdr {
612 pub ssn_id: u64,
613 pub tree_id: u32,
614 pub rec_type: u32,
615 pub msg_id: u64,
616}
617
618impl SMBCommonHdr {
02dccb15
JL
619 pub fn init() -> Self {
620 Default::default()
75d7c9d6 621 }
02dccb15
JL
622 pub fn new(rec_type: u32, ssn_id: u64, tree_id: u32, msg_id: u64) -> Self {
623 Self {
75d7c9d6
VJ
624 rec_type : rec_type,
625 ssn_id : ssn_id,
626 tree_id : tree_id,
627 msg_id : msg_id,
628 }
629 }
630 pub fn from2(r: &Smb2Record, rec_type: u32) -> SMBCommonHdr {
631 let tree_id = match rec_type {
632 SMBHDR_TYPE_TREE => { 0 },
633 _ => r.tree_id,
634 };
635 let msg_id = match rec_type {
be615c9f 636 SMBHDR_TYPE_TRANS_FRAG | SMBHDR_TYPE_SHARE => { 0 },
75d7c9d6
VJ
637 _ => { r.message_id as u64 },
638 };
639
640 SMBCommonHdr {
641 rec_type : rec_type,
642 ssn_id : r.session_id,
643 tree_id : tree_id,
644 msg_id : msg_id,
645 }
646
647 }
648 pub fn from1(r: &SmbRecord, rec_type: u32) -> SMBCommonHdr {
649 let tree_id = match rec_type {
650 SMBHDR_TYPE_TREE => { 0 },
651 _ => r.tree_id as u32,
652 };
653 let msg_id = match rec_type {
be615c9f 654 SMBHDR_TYPE_TRANS_FRAG | SMBHDR_TYPE_SHARE => { 0 },
75d7c9d6
VJ
655 _ => { r.multiplex_id as u64 },
656 };
657
658 SMBCommonHdr {
659 rec_type : rec_type,
660 ssn_id : r.ssn_id as u64,
661 tree_id : tree_id,
662 msg_id : msg_id,
663 }
664 }
8b570c02
VJ
665
666 // don't include tree id
667 pub fn compare(&self, hdr: &SMBCommonHdr) -> bool {
a729d266
VJ
668 self.rec_type == hdr.rec_type && self.ssn_id == hdr.ssn_id &&
669 self.msg_id == hdr.msg_id
8b570c02 670 }
75d7c9d6
VJ
671}
672
673#[derive(Hash, Eq, PartialEq, Debug)]
674pub struct SMBHashKeyHdrGuid {
675 hdr: SMBCommonHdr,
676 guid: Vec<u8>,
677}
678
679impl SMBHashKeyHdrGuid {
02dccb15
JL
680 pub fn new(hdr: SMBCommonHdr, guid: Vec<u8>) -> Self {
681 Self {
75d7c9d6
VJ
682 hdr: hdr, guid: guid,
683 }
684 }
685}
686
687#[derive(Hash, Eq, PartialEq, Debug)]
688pub struct SMBTree {
689 pub name: Vec<u8>,
690 pub is_pipe: bool,
691}
692
693impl SMBTree {
02dccb15
JL
694 pub fn new(name: Vec<u8>, is_pipe: bool) -> Self {
695 Self {
75d7c9d6
VJ
696 name:name,
697 is_pipe:is_pipe,
698 }
699 }
700}
701
702pub fn u32_as_bytes(i: u32) -> [u8;4] {
703 let o1: u8 = ((i >> 24) & 0xff) as u8;
704 let o2: u8 = ((i >> 16) & 0xff) as u8;
705 let o3: u8 = ((i >> 8) & 0xff) as u8;
706 let o4: u8 = (i & 0xff) as u8;
707 return [o1, o2, o3, o4]
708}
709
710pub struct SMBState<> {
711 /// map ssn/tree/msgid to vec (guid/name/share)
712 pub ssn2vec_map: HashMap<SMBCommonHdr, Vec<u8>>,
713 /// map guid to filename
714 pub guid2name_map: HashMap<Vec<u8>, Vec<u8>>,
715 /// map ssn key to read offset
716 pub ssn2vecoffset_map: HashMap<SMBCommonHdr, SMBFileGUIDOffset>,
717
718 pub ssn2tree_map: HashMap<SMBCommonHdr, SMBTree>,
719
4d502425
VJ
720 // store partial data records that are transfered in multiple
721 // requests for DCERPC.
75d7c9d6
VJ
722 pub ssnguid2vec_map: HashMap<SMBHashKeyHdrGuid, Vec<u8>>,
723
5226ba1c 724 pub files: Files,
75d7c9d6 725
aa8d64c2
VJ
726 skip_ts: u32,
727 skip_tc: u32,
75d7c9d6
VJ
728
729 pub file_ts_left : u32,
730 pub file_tc_left : u32,
731 pub file_ts_guid : Vec<u8>,
732 pub file_tc_guid : Vec<u8>,
733
734 pub ts_ssn_gap: bool,
735 pub tc_ssn_gap: bool,
736
737 pub ts_gap: bool, // last TS update was gap
738 pub tc_gap: bool, // last TC update was gap
739
8bef1208
VJ
740 pub ts_trunc: bool, // no more data for TOSERVER
741 pub tc_trunc: bool, // no more data for TOCLIENT
75d7c9d6 742
d41aecce
VJ
743 /// true as long as we have file txs that are in a post-gap
744 /// state. It means we'll do extra house keeping for those.
745 check_post_gap_file_txs: bool,
25f2efe9 746 post_gap_files_checked: bool,
d41aecce 747
75d7c9d6
VJ
748 /// transactions list
749 pub transactions: Vec<SMBTransaction>,
750
751 /// tx counter for assigning incrementing id's to tx's
752 tx_id: u64,
753
0d69e7b8 754 /// SMB2 dialect or 0 if not set or SMB1
75d7c9d6 755 pub dialect: u16,
0d69e7b8 756 /// contains name of SMB1 dialect
75d7c9d6 757 pub dialect_vec: Option<Vec<u8>>, // used if dialect == 0
75d7c9d6
VJ
758
759 /// dcerpc interfaces, stored here to be able to match
760 /// them while inspecting DCERPC REQUEST txs
761 pub dcerpc_ifaces: Option<Vec<DCERPCIface>>,
d41aecce
VJ
762
763 /// Timestamp in seconds of last update. This is packet time,
764 /// potentially coming from pcaps.
765 ts: u64,
75d7c9d6
VJ
766}
767
fcfc9876
JI
768impl State<SMBTransaction> for SMBState {
769 fn get_transactions(&self) -> &[SMBTransaction] {
770 &self.transactions
771 }
772}
773
75d7c9d6
VJ
774impl SMBState {
775 /// Allocation function for a new TLS parser instance
02dccb15
JL
776 pub fn new() -> Self {
777 Self {
75d7c9d6
VJ
778 ssn2vec_map:HashMap::new(),
779 guid2name_map:HashMap::new(),
780 ssn2vecoffset_map:HashMap::new(),
781 ssn2tree_map:HashMap::new(),
75d7c9d6 782 ssnguid2vec_map:HashMap::new(),
3587033d 783 files: Files::default(),
75d7c9d6
VJ
784 skip_ts:0,
785 skip_tc:0,
786 file_ts_left:0,
787 file_tc_left:0,
788 file_ts_guid:Vec::new(),
789 file_tc_guid:Vec::new(),
790 ts_ssn_gap: false,
791 tc_ssn_gap: false,
792 ts_gap: false,
793 tc_gap: false,
794 ts_trunc: false,
795 tc_trunc: false,
d41aecce 796 check_post_gap_file_txs: false,
25f2efe9 797 post_gap_files_checked: false,
75d7c9d6
VJ
798 transactions: Vec::new(),
799 tx_id:0,
800 dialect:0,
801 dialect_vec: None,
75d7c9d6 802 dcerpc_ifaces: None,
d41aecce 803 ts: 0,
75d7c9d6
VJ
804 }
805 }
806
807 pub fn free(&mut self) {
808 //self._debug_state_stats();
809 self._debug_tx_stats();
75d7c9d6
VJ
810 }
811
812 pub fn new_tx(&mut self) -> SMBTransaction {
813 let mut tx = SMBTransaction::new();
814 self.tx_id += 1;
815 tx.id = self.tx_id;
816 SCLogDebug!("TX {} created", tx.id);
817 return tx;
818 }
819
820 pub fn free_tx(&mut self, tx_id: u64) {
821 SCLogDebug!("Freeing TX with ID {} TX.ID {}", tx_id, tx_id+1);
822 let len = self.transactions.len();
823 let mut found = false;
824 let mut index = 0;
825 for i in 0..len {
826 let tx = &self.transactions[i];
827 if tx.id == tx_id + 1 {
828 found = true;
829 index = i;
830 SCLogDebug!("tx {} progress {}/{}", tx.id, tx.request_done, tx.response_done);
831 break;
832 }
833 }
834 if found {
835 SCLogDebug!("freeing TX with ID {} TX.ID {} at index {} left: {} max id: {}",
836 tx_id, tx_id+1, index, self.transactions.len(), self.tx_id);
837 self.transactions.remove(index);
838 }
839 }
840
75d7c9d6
VJ
841 pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SMBTransaction> {
842/*
843 if self.transactions.len() > 100 {
844 SCLogNotice!("get_tx_by_id: tx_id={} in list={}", tx_id, self.transactions.len());
845 self._dump_txs();
846 panic!("txs exploded");
847 }
848*/
849 for tx in &mut self.transactions {
850 if tx.id == tx_id + 1 {
851 let ver = tx.vercmd.get_version();
852 let mut _smbcmd;
853 if ver == 2 {
854 let (_, cmd) = tx.vercmd.get_smb2_cmd();
855 _smbcmd = cmd;
856 } else {
857 let (_, cmd) = tx.vercmd.get_smb1_cmd();
858 _smbcmd = cmd as u16;
859 }
860 SCLogDebug!("Found SMB TX: id {} ver:{} cmd:{} progress {}/{} type_data {:?}",
861 tx.id, ver, _smbcmd, tx.request_done, tx.response_done, tx.type_data);
862 return Some(tx);
863 }
864 }
865 SCLogDebug!("Failed to find SMB TX with ID {}", tx_id);
866 return None;
867 }
868
8aa38060
VJ
869 fn update_ts(&mut self, ts: u64) {
870 if ts != self.ts {
871 self.ts = ts;
25f2efe9 872 self.post_gap_files_checked = false;
8aa38060
VJ
873 }
874 }
875
75d7c9d6
VJ
876 /* generic TX has no type_data and is only used to
877 * track a single cmd request/reply pair. */
878
879 pub fn new_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: SMBCommonHdr)
5ee83230 880 -> &mut SMBTransaction
75d7c9d6
VJ
881 {
882 let mut tx = self.new_tx();
883 if smb_ver == 1 && smb_cmd <= 255 {
884 tx.vercmd.set_smb1_cmd(smb_cmd as u8);
885 } else if smb_ver == 2 {
886 tx.vercmd.set_smb2_cmd(smb_cmd);
887 }
888
889 tx.type_data = None;
890 tx.request_done = true;
891 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
892 tx.hdr = key;
893
894 SCLogDebug!("SMB: TX GENERIC created: ID {} tx list {} {:?}",
895 tx.id, self.transactions.len(), &tx);
896 self.transactions.push(tx);
897 let tx_ref = self.transactions.last_mut();
898 return tx_ref.unwrap();
899 }
900
901 pub fn get_last_tx(&mut self, smb_ver: u8, smb_cmd: u16)
902 -> Option<&mut SMBTransaction>
903 {
904 let tx_ref = self.transactions.last_mut();
905 match tx_ref {
906 Some(tx) => {
907 let found = if tx.vercmd.get_version() == smb_ver {
908 if smb_ver == 1 {
909 let (_, cmd) = tx.vercmd.get_smb1_cmd();
910 cmd as u16 == smb_cmd
911 } else if smb_ver == 2 {
912 let (_, cmd) = tx.vercmd.get_smb2_cmd();
913 cmd == smb_cmd
914 } else {
915 false
916 }
917 } else {
918 false
919 };
920 if found {
921 return Some(tx);
922 }
923 },
924 None => { },
925 }
926 return None;
927 }
928
929 pub fn get_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: &SMBCommonHdr)
930 -> Option<&mut SMBTransaction>
931 {
932 for tx in &mut self.transactions {
933 let found = if tx.vercmd.get_version() == smb_ver {
934 if smb_ver == 1 {
935 let (_, cmd) = tx.vercmd.get_smb1_cmd();
8b570c02 936 cmd as u16 == smb_cmd && tx.hdr.compare(key)
75d7c9d6
VJ
937 } else if smb_ver == 2 {
938 let (_, cmd) = tx.vercmd.get_smb2_cmd();
8b570c02 939 cmd == smb_cmd && tx.hdr.compare(key)
75d7c9d6
VJ
940 } else {
941 false
942 }
943 } else {
944 false
945 };
946 if found {
947 return Some(tx);
948 }
949 }
950 return None;
951 }
952
953 pub fn new_negotiate_tx(&mut self, smb_ver: u8)
5ee83230 954 -> &mut SMBTransaction
75d7c9d6
VJ
955 {
956 let mut tx = self.new_tx();
957 if smb_ver == 1 {
958 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_NEGOTIATE_PROTOCOL);
959 } else if smb_ver == 2 {
960 tx.vercmd.set_smb2_cmd(SMB2_COMMAND_NEGOTIATE_PROTOCOL);
961 }
962
963 tx.type_data = Some(SMBTransactionTypeData::NEGOTIATE(
964 SMBTransactionNegotiate::new(smb_ver)));
965 tx.request_done = true;
966 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
967
968 SCLogDebug!("SMB: TX NEGOTIATE created: ID {} SMB ver {}", tx.id, smb_ver);
969 self.transactions.push(tx);
970 let tx_ref = self.transactions.last_mut();
971 return tx_ref.unwrap();
972 }
973
974 pub fn get_negotiate_tx(&mut self, smb_ver: u8)
975 -> Option<&mut SMBTransaction>
976 {
977 for tx in &mut self.transactions {
978 let found = match tx.type_data {
979 Some(SMBTransactionTypeData::NEGOTIATE(ref x)) => {
980 if x.smb_ver == smb_ver {
981 true
982 } else {
983 false
984 }
985 },
986 _ => { false },
987 };
988 if found {
989 return Some(tx);
990 }
991 }
992 return None;
993 }
994
995 pub fn new_treeconnect_tx(&mut self, hdr: SMBCommonHdr, name: Vec<u8>)
5ee83230 996 -> &mut SMBTransaction
75d7c9d6
VJ
997 {
998 let mut tx = self.new_tx();
999
1000 tx.hdr = hdr;
1001 tx.type_data = Some(SMBTransactionTypeData::TREECONNECT(
1002 SMBTransactionTreeConnect::new(name.to_vec())));
1003 tx.request_done = true;
1004 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
1005
1006 SCLogDebug!("SMB: TX TREECONNECT created: ID {} NAME {}",
1007 tx.id, String::from_utf8_lossy(&name));
1008 self.transactions.push(tx);
1009 let tx_ref = self.transactions.last_mut();
1010 return tx_ref.unwrap();
1011 }
1012
1013 pub fn get_treeconnect_tx(&mut self, hdr: SMBCommonHdr)
1014 -> Option<&mut SMBTransaction>
1015 {
1016 for tx in &mut self.transactions {
8b570c02 1017 let hit = tx.hdr.compare(&hdr) && match tx.type_data {
75d7c9d6
VJ
1018 Some(SMBTransactionTypeData::TREECONNECT(_)) => { true },
1019 _ => { false },
1020 };
1021 if hit {
1022 return Some(tx);
1023 }
1024 }
1025 return None;
1026 }
1027
1028 pub fn new_create_tx(&mut self, file_name: &Vec<u8>,
1029 disposition: u32, del: bool, dir: bool,
1030 hdr: SMBCommonHdr)
1031 -> &mut SMBTransaction
1032 {
1033 let mut tx = self.new_tx();
1034 tx.hdr = hdr;
1035 tx.type_data = Some(SMBTransactionTypeData::CREATE(
1036 SMBTransactionCreate::new(
1037 file_name.to_vec(), disposition,
1038 del, dir)));
1039 tx.request_done = true;
1040 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
1041
1042 self.transactions.push(tx);
1043 let tx_ref = self.transactions.last_mut();
1044 return tx_ref.unwrap();
1045 }
1046
1047 pub fn get_create_tx_by_hdr(&mut self, hdr: &SMBCommonHdr)
1048 -> Option<&mut SMBTransaction>
1049 {
1050 for tx in &mut self.transactions {
1051 let found = match tx.type_data {
1052 Some(SMBTransactionTypeData::CREATE(ref _d)) => {
69cf5c9e 1053 tx.hdr.compare(hdr)
75d7c9d6
VJ
1054 },
1055 _ => { false },
1056 };
1057
1058 if found {
1059 SCLogDebug!("SMB: Found SMB create TX with ID {}", tx.id);
1060 return Some(tx);
1061 }
1062 }
1063 SCLogDebug!("SMB: Failed to find SMB create TX with key {:?}", hdr);
1064 return None;
1065 }
1066
1067 pub fn get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool)
1068 {
1069 let (name, is_dcerpc) = match self.guid2name_map.get(&guid.to_vec()) {
1070 Some(n) => {
1b86d4e1
VJ
1071 let mut s = n.as_slice();
1072 // skip leading \ if we have it
1073 if s.len() > 1 && s[0] == 0x5c_u8 {
1074 s = &s[1..];
1075 }
1076 match str::from_utf8(s) {
75d7c9d6
VJ
1077 Ok("PSEXESVC") => ("PSEXESVC", false),
1078 Ok("svcctl") => ("svcctl", true),
1079 Ok("srvsvc") => ("srvsvc", true),
1080 Ok("atsvc") => ("atsvc", true),
1081 Ok("lsarpc") => ("lsarpc", true),
1082 Ok("samr") => ("samr", true),
1b86d4e1 1083 Ok("spoolss") => ("spoolss", true),
a7ee2ffb 1084 Ok("winreg") => ("winreg", true),
ac4e8885 1085 Ok("suricata::dcerpc") => ("unknown", true),
75d7c9d6
VJ
1086 Err(_) => ("MALFORMED", false),
1087 Ok(&_) => {
ea1e13cb 1088 SCLogDebug!("don't know {}", String::from_utf8_lossy(&n));
75d7c9d6
VJ
1089 ("UNKNOWN", false)
1090 },
1091 }
1092 },
1093 _ => { ("UNKNOWN", false) },
1094 };
1095 SCLogDebug!("service {} is_dcerpc {}", name, is_dcerpc);
69cf5c9e 1096 (name, is_dcerpc)
75d7c9d6
VJ
1097 }
1098
d41aecce
VJ
1099 fn post_gap_housekeeping_for_files(&mut self)
1100 {
1101 let mut post_gap_txs = false;
1102 for tx in &mut self.transactions {
65e9a7c3 1103 if let Some(SMBTransactionTypeData::FILE(ref mut f)) = tx.type_data {
d41aecce
VJ
1104 if f.post_gap_ts > 0 {
1105 if self.ts > f.post_gap_ts {
1106 tx.request_done = true;
1107 tx.response_done = true;
89cb3379 1108 let (files, flags) = self.files.get(f.direction);
65e9a7c3 1109 f.file_tracker.trunc(files, flags);
d41aecce
VJ
1110 } else {
1111 post_gap_txs = true;
1112 }
1113 }
1114 }
1115 }
1116 self.check_post_gap_file_txs = post_gap_txs;
1117 }
1118
44ac3e30
VJ
1119 /* after a gap we will consider all transactions complete for our
1120 * direction. File transfer transactions are an exception. Those
d41aecce
VJ
1121 * can handle gaps. For the file transactions we set the current
1122 * (flow) time and prune them in 60 seconds if no update for them
1123 * was received. */
89cb3379 1124 fn post_gap_housekeeping(&mut self, dir: Direction)
75d7c9d6 1125 {
89cb3379 1126 if self.ts_ssn_gap && dir == Direction::ToServer {
44ac3e30
VJ
1127 for tx in &mut self.transactions {
1128 if tx.id >= self.tx_id {
1129 SCLogDebug!("post_gap_housekeeping: done");
1130 break;
1131 }
d41aecce
VJ
1132 if let Some(SMBTransactionTypeData::FILE(ref mut f)) = tx.type_data {
1133 // leaving FILE txs open as they can deal with gaps. We
1134 // remove them after 60 seconds of no activity though.
1135 if f.post_gap_ts == 0 {
1136 f.post_gap_ts = self.ts + 60;
1137 self.check_post_gap_file_txs = true;
1138 }
44ac3e30
VJ
1139 } else {
1140 SCLogDebug!("post_gap_housekeeping: tx {} marked as done TS", tx.id);
1141 tx.request_done = true;
1142 }
a44504a1 1143 }
89cb3379 1144 } else if self.tc_ssn_gap && dir == Direction::ToClient {
44ac3e30
VJ
1145 for tx in &mut self.transactions {
1146 if tx.id >= self.tx_id {
1147 SCLogDebug!("post_gap_housekeeping: done");
1148 break;
1149 }
d41aecce
VJ
1150 if let Some(SMBTransactionTypeData::FILE(ref mut f)) = tx.type_data {
1151 // leaving FILE txs open as they can deal with gaps. We
1152 // remove them after 60 seconds of no activity though.
1153 if f.post_gap_ts == 0 {
1154 f.post_gap_ts = self.ts + 60;
1155 self.check_post_gap_file_txs = true;
1156 }
44ac3e30
VJ
1157 } else {
1158 SCLogDebug!("post_gap_housekeeping: tx {} marked as done TC", tx.id);
1159 tx.request_done = true;
1160 tx.response_done = true;
1161 }
75d7c9d6 1162 }
44ac3e30 1163
75d7c9d6
VJ
1164 }
1165 }
1166
89cb3379 1167 pub fn set_file_left(&mut self, direction: Direction, rec_size: u32, data_size: u32, fuid: Vec<u8>)
4d58aaae 1168 {
f40fc029 1169 let left = rec_size.saturating_sub(data_size);
89cb3379 1170 if direction == Direction::ToServer {
4d58aaae
VJ
1171 self.file_ts_left = left;
1172 self.file_ts_guid = fuid;
1173 } else {
1174 self.file_tc_left = left;
1175 self.file_tc_guid = fuid;
1176 }
1177 }
1178
89cb3379 1179 pub fn set_skip(&mut self, direction: Direction, rec_size: u32, data_size: u32)
aa8d64c2 1180 {
f40fc029 1181 let skip = rec_size.saturating_sub(data_size);
89cb3379 1182 if direction == Direction::ToServer {
aa8d64c2
VJ
1183 self.skip_ts = skip;
1184 } else {
1185 self.skip_tc = skip;
1186 }
1187 }
1188
75d7c9d6 1189 // return how much data we consumed
89cb3379
SB
1190 fn handle_skip(&mut self, direction: Direction, input_size: u32) -> u32 {
1191 let mut skip_left = if direction == Direction::ToServer {
75d7c9d6
VJ
1192 self.skip_ts
1193 } else {
1194 self.skip_tc
1195 };
1196 if skip_left == 0 {
1197 return 0
1198 }
1199 SCLogDebug!("skip_left {} input_size {}", skip_left, input_size);
1200
1201 let consumed = if skip_left >= input_size {
1202 input_size
1203 } else {
1204 skip_left
1205 };
1206
1207 if skip_left <= input_size {
1208 skip_left = 0;
1209 } else {
1210 skip_left -= input_size;
1211 }
1212
89cb3379 1213 if direction == Direction::ToServer {
75d7c9d6
VJ
1214 self.skip_ts = skip_left;
1215 } else {
1216 self.skip_tc = skip_left;
1217 }
1218 return consumed;
1219 }
1220
1221 /// return bytes consumed
1222 pub fn parse_tcp_data_ts_partial<'b>(&mut self, input: &'b[u8]) -> usize
1223 {
1224 SCLogDebug!("incomplete of size {}", input.len());
1225 if input.len() < 512 {
7dff9b99
VJ
1226 // check for malformed data. Wireshark reports as
1227 // 'NBSS continuation data'. If it's invalid we're
1228 // lost so we give up.
1229 if input.len() > 8 {
1230 match parse_nbss_record_partial(input) {
13b73997 1231 Ok((_, ref hdr)) => {
7dff9b99
VJ
1232 if !hdr.is_smb() {
1233 SCLogDebug!("partial NBSS, not SMB and no known msg type {}", hdr.message_type);
1234 self.trunc_ts();
1235 return 0;
1236 }
1237 },
1238 _ => {},
1239 }
1240 }
75d7c9d6
VJ
1241 return 0;
1242 }
1243
1244 match parse_nbss_record_partial(input) {
13b73997 1245 Ok((output, ref nbss_part_hdr)) => {
75d7c9d6
VJ
1246 SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len());
1247 if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
69cf5c9e 1248 match parse_smb_version(nbss_part_hdr.data) {
13b73997 1249 Ok((_, ref smb)) => {
75d7c9d6 1250 SCLogDebug!("SMB {:?}", smb);
b3439205 1251 if smb.version == 0xff_u8 { // SMB1
75d7c9d6 1252 SCLogDebug!("SMBv1 record");
69cf5c9e 1253 match parse_smb_record(nbss_part_hdr.data) {
13b73997 1254 Ok((_, ref r)) => {
75d7c9d6
VJ
1255 if r.command == SMB1_COMMAND_WRITE_ANDX {
1256 // see if it's a write to a pipe. We only handle those
1257 // if complete.
1258 let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
1259 r.ssn_id as u64, r.tree_id as u32, 0);
1260 let is_pipe = match self.ssn2tree_map.get(&tree_key) {
1261 Some(n) => n.is_pipe,
1262 None => false,
1263 };
1264 if is_pipe {
1265 return 0;
1266 }
2d146062 1267 smb1_write_request_record(self, r, SMB1_HEADER_SIZE, SMB1_COMMAND_WRITE_ANDX);
75d7c9d6
VJ
1268 let consumed = input.len() - output.len();
1269 return consumed;
1270 }
1271 },
1272 _ => { },
1273
1274 }
b3439205 1275 } else if smb.version == 0xfe_u8 { // SMB2
4bf87d30 1276 SCLogDebug!("NBSS record {:?}", nbss_part_hdr);
75d7c9d6 1277 SCLogDebug!("SMBv2 record");
69cf5c9e 1278 match parse_smb2_request_record(nbss_part_hdr.data) {
13b73997 1279 Ok((_, ref smb_record)) => {
75d7c9d6
VJ
1280 SCLogDebug!("SMB2: partial record {}",
1281 &smb2_command_string(smb_record.command));
1282 if smb_record.command == SMB2_COMMAND_WRITE {
1283 smb2_write_request_record(self, smb_record);
1284 let consumed = input.len() - output.len();
4bf87d30 1285 SCLogDebug!("consumed {}", consumed);
75d7c9d6
VJ
1286 return consumed;
1287 }
1288 },
1289 _ => { },
1290 }
1291 }
b3439205 1292 // no SMB3 here yet, will buffer full records
75d7c9d6
VJ
1293 },
1294 _ => { },
1295 }
1296 }
1297 },
1298 _ => { },
1299 }
1300
1301 return 0;
1302 }
1303
1304 /// Parsing function, handling TCP chunks fragmentation
4bf87d30 1305 pub fn parse_tcp_data_ts<'b>(&mut self, i: &'b[u8]) -> AppLayerResult
75d7c9d6 1306 {
4bf87d30 1307 let mut cur_i = i;
89cb3379 1308 let consumed = self.handle_skip(Direction::ToServer, cur_i.len() as u32);
75d7c9d6
VJ
1309 if consumed > 0 {
1310 if consumed > cur_i.len() as u32 {
1311 self.set_event(SMBEvent::InternalError);
4bf87d30 1312 return AppLayerResult::err();
75d7c9d6
VJ
1313 }
1314 cur_i = &cur_i[consumed as usize..];
1315 }
1316 // take care of in progress file chunk transfers
1317 // and skip buffer beyond it
89cb3379 1318 let consumed = self.filetracker_update(Direction::ToServer, cur_i, 0);
75d7c9d6
VJ
1319 if consumed > 0 {
1320 if consumed > cur_i.len() as u32 {
1321 self.set_event(SMBEvent::InternalError);
4bf87d30 1322 return AppLayerResult::err();
75d7c9d6
VJ
1323 }
1324 cur_i = &cur_i[consumed as usize..];
1325 }
4bf87d30
VJ
1326 if cur_i.len() == 0 {
1327 return AppLayerResult::ok();
1328 }
75d7c9d6
VJ
1329 // gap
1330 if self.ts_gap {
a44504a1 1331 SCLogDebug!("TS trying to catch up after GAP (input {})", cur_i.len());
4bf87d30
VJ
1332 while cur_i.len() > 0 { // min record size
1333 match search_smb_record(cur_i) {
1334 Ok((_, pg)) => {
1335 SCLogDebug!("smb record found");
1336 let smb2_offset = cur_i.len() - pg.len();
1337 if smb2_offset < 4 {
1338 cur_i = &cur_i[smb2_offset+4..];
1339 continue; // see if we have another record in our data
1340 }
1341 let nbss_offset = smb2_offset - 4;
1342 cur_i = &cur_i[nbss_offset..];
75d7c9d6 1343
4bf87d30
VJ
1344 self.ts_gap = false;
1345 break;
1346 },
1347 _ => {
1348 let mut consumed = i.len();
1349 if consumed < 4 {
1350 consumed = 0;
1351 } else {
1352 consumed = consumed - 3;
1353 }
1354 SCLogDebug!("smb record NOT found");
1355 return AppLayerResult::incomplete(consumed as u32, 8);
1356 },
1357 }
75d7c9d6
VJ
1358 }
1359 }
1360 while cur_i.len() > 0 { // min record size
1361 match parse_nbss_record(cur_i) {
13b73997 1362 Ok((rem, ref nbss_hdr)) => {
75d7c9d6
VJ
1363 if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1364 // we have the full records size worth of data,
1365 // let's parse it
69cf5c9e 1366 match parse_smb_version(nbss_hdr.data) {
13b73997 1367 Ok((_, ref smb)) => {
75d7c9d6 1368 SCLogDebug!("SMB {:?}", smb);
b3439205 1369 if smb.version == 0xff_u8 { // SMB1
75d7c9d6 1370 SCLogDebug!("SMBv1 record");
69cf5c9e 1371 match parse_smb_record(nbss_hdr.data) {
13b73997 1372 Ok((_, ref smb_record)) => {
75d7c9d6
VJ
1373 smb1_request_record(self, smb_record);
1374 },
1375 _ => {
1376 self.set_event(SMBEvent::MalformedData);
4bf87d30 1377 return AppLayerResult::err();
75d7c9d6
VJ
1378 },
1379 }
b3439205 1380 } else if smb.version == 0xfe_u8 { // SMB2
75d7c9d6
VJ
1381 let mut nbss_data = nbss_hdr.data;
1382 while nbss_data.len() > 0 {
1383 SCLogDebug!("SMBv2 record");
69cf5c9e 1384 match parse_smb2_request_record(nbss_data) {
13b73997 1385 Ok((nbss_data_rem, ref smb_record)) => {
75d7c9d6
VJ
1386 SCLogDebug!("nbss_data_rem {}", nbss_data_rem.len());
1387
1388 smb2_request_record(self, smb_record);
1389 nbss_data = nbss_data_rem;
1390 },
1391 _ => {
1392 self.set_event(SMBEvent::MalformedData);
4bf87d30 1393 return AppLayerResult::err();
75d7c9d6
VJ
1394 },
1395 }
1396 }
b3439205
VJ
1397 } else if smb.version == 0xfd_u8 { // SMB3 transform
1398 let mut nbss_data = nbss_hdr.data;
1399 while nbss_data.len() > 0 {
1400 SCLogDebug!("SMBv3 transform record");
69cf5c9e 1401 match parse_smb3_transform_record(nbss_data) {
13b73997 1402 Ok((nbss_data_rem, ref _smb3_record)) => {
b3439205
VJ
1403 nbss_data = nbss_data_rem;
1404 },
1405 _ => {
1406 self.set_event(SMBEvent::MalformedData);
4bf87d30 1407 return AppLayerResult::err();
b3439205
VJ
1408 },
1409 }
1410 }
75d7c9d6
VJ
1411 }
1412 },
1413 _ => {
1414 self.set_event(SMBEvent::MalformedData);
4bf87d30 1415 return AppLayerResult::err();
75d7c9d6
VJ
1416 },
1417 }
1418 } else {
1419 SCLogDebug!("NBSS message {:X}", nbss_hdr.message_type);
1420 }
1421 cur_i = rem;
1422 },
4bf87d30
VJ
1423 Err(nom::Err::Incomplete(needed)) => {
1424 if let nom::Needed::Size(n) = needed {
1425 // 512 is the minimum for parse_tcp_data_ts_partial
1426 if n >= 512 && cur_i.len() < 512 {
1427 let total_consumed = i.len() - cur_i.len();
1428 return AppLayerResult::incomplete(total_consumed as u32, 512);
1429 }
1430 let consumed = self.parse_tcp_data_ts_partial(cur_i);
1431 if consumed == 0 {
1432 // if we consumed none we will buffer the entire record
1433 let total_consumed = i.len() - cur_i.len();
1434 SCLogDebug!("setting consumed {} need {} needed {:?} total input {}",
1435 total_consumed, n, needed, i.len());
1436 let need = n + 4; // Incomplete returns size of data minus NBSS header
1437 return AppLayerResult::incomplete(total_consumed as u32, need as u32);
1438 }
1439 // tracking a write record, which we don't need to
1440 // queue up at the stream level, but can feed to us
1441 // in small chunks
1442 return AppLayerResult::ok();
1443 } else {
1444 self.set_event(SMBEvent::InternalError);
1445 return AppLayerResult::err();
1446 }
75d7c9d6 1447 },
13b73997 1448 Err(_) => {
75d7c9d6 1449 self.set_event(SMBEvent::MalformedData);
4bf87d30 1450 return AppLayerResult::err();
75d7c9d6
VJ
1451 },
1452 }
1453 };
1454
89cb3379 1455 self.post_gap_housekeeping(Direction::ToServer);
25f2efe9 1456 if self.check_post_gap_file_txs && !self.post_gap_files_checked {
d41aecce 1457 self.post_gap_housekeeping_for_files();
25f2efe9 1458 self.post_gap_files_checked = true;
d41aecce 1459 }
4bf87d30 1460 AppLayerResult::ok()
75d7c9d6
VJ
1461 }
1462
1463 /// return bytes consumed
1464 pub fn parse_tcp_data_tc_partial<'b>(&mut self, input: &'b[u8]) -> usize
1465 {
1466 SCLogDebug!("incomplete of size {}", input.len());
1467 if input.len() < 512 {
7dff9b99
VJ
1468 // check for malformed data. Wireshark reports as
1469 // 'NBSS continuation data'. If it's invalid we're
1470 // lost so we give up.
1471 if input.len() > 8 {
1472 match parse_nbss_record_partial(input) {
13b73997 1473 Ok((_, ref hdr)) => {
7dff9b99
VJ
1474 if !hdr.is_smb() {
1475 SCLogDebug!("partial NBSS, not SMB and no known msg type {}", hdr.message_type);
1476 self.trunc_tc();
1477 return 0;
1478 }
1479 },
1480 _ => {},
1481 }
1482 }
75d7c9d6
VJ
1483 return 0;
1484 }
1485
1486 match parse_nbss_record_partial(input) {
13b73997 1487 Ok((output, ref nbss_part_hdr)) => {
75d7c9d6
VJ
1488 SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len());
1489 if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
69cf5c9e 1490 match parse_smb_version(nbss_part_hdr.data) {
13b73997 1491 Ok((_, ref smb)) => {
75d7c9d6
VJ
1492 SCLogDebug!("SMB {:?}", smb);
1493 if smb.version == 255u8 { // SMB1
1494 SCLogDebug!("SMBv1 record");
69cf5c9e 1495 match parse_smb_record(nbss_part_hdr.data) {
13b73997 1496 Ok((_, ref r)) => {
75d7c9d6
VJ
1497 SCLogDebug!("SMB1: partial record {}",
1498 r.command);
1499 if r.command == SMB1_COMMAND_READ_ANDX {
1500 let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
1501 r.ssn_id as u64, r.tree_id as u32, 0);
1502 let is_pipe = match self.ssn2tree_map.get(&tree_key) {
1503 Some(n) => n.is_pipe,
1504 None => false,
1505 };
1506 if is_pipe {
1507 return 0;
1508 }
2d146062 1509 smb1_read_response_record(self, r, SMB1_HEADER_SIZE);
75d7c9d6
VJ
1510 let consumed = input.len() - output.len();
1511 return consumed;
1512 }
1513 },
1514 _ => { },
1515 }
1516 } else if smb.version == 254u8 { // SMB2
1517 SCLogDebug!("SMBv2 record");
69cf5c9e 1518 match parse_smb2_response_record(nbss_part_hdr.data) {
13b73997 1519 Ok((_, ref smb_record)) => {
75d7c9d6
VJ
1520 SCLogDebug!("SMB2: partial record {}",
1521 &smb2_command_string(smb_record.command));
1522 if smb_record.command == SMB2_COMMAND_READ {
1523 smb2_read_response_record(self, smb_record);
1524 let consumed = input.len() - output.len();
1525 return consumed;
1526 }
1527 },
1528 _ => { },
1529 }
1530 }
b3439205 1531 // no SMB3 here yet, will buffer full records
75d7c9d6
VJ
1532 },
1533 _ => { },
1534 }
1535 }
1536 },
1537 _ => { },
1538 }
1539
1540 return 0;
1541 }
1542
1543 /// Parsing function, handling TCP chunks fragmentation
4bf87d30 1544 pub fn parse_tcp_data_tc<'b>(&mut self, i: &'b[u8]) -> AppLayerResult
75d7c9d6 1545 {
4bf87d30 1546 let mut cur_i = i;
89cb3379 1547 let consumed = self.handle_skip(Direction::ToClient, cur_i.len() as u32);
75d7c9d6
VJ
1548 if consumed > 0 {
1549 if consumed > cur_i.len() as u32 {
1550 self.set_event(SMBEvent::InternalError);
4bf87d30 1551 return AppLayerResult::err();
75d7c9d6
VJ
1552 }
1553 cur_i = &cur_i[consumed as usize..];
1554 }
1555 // take care of in progress file chunk transfers
1556 // and skip buffer beyond it
89cb3379 1557 let consumed = self.filetracker_update(Direction::ToClient, cur_i, 0);
75d7c9d6
VJ
1558 if consumed > 0 {
1559 if consumed > cur_i.len() as u32 {
1560 self.set_event(SMBEvent::InternalError);
4bf87d30 1561 return AppLayerResult::err();
75d7c9d6
VJ
1562 }
1563 cur_i = &cur_i[consumed as usize..];
1564 }
4bf87d30
VJ
1565 if cur_i.len() == 0 {
1566 return AppLayerResult::ok();
1567 }
75d7c9d6
VJ
1568 // gap
1569 if self.tc_gap {
b3439205 1570 SCLogDebug!("TC trying to catch up after GAP (input {})", cur_i.len());
4bf87d30
VJ
1571 while cur_i.len() > 0 { // min record size
1572 match search_smb_record(cur_i) {
1573 Ok((_, pg)) => {
1574 SCLogDebug!("smb record found");
1575 let smb2_offset = cur_i.len() - pg.len();
1576 if smb2_offset < 4 {
1577 cur_i = &cur_i[smb2_offset+4..];
1578 continue; // see if we have another record in our data
1579 }
1580 let nbss_offset = smb2_offset - 4;
1581 cur_i = &cur_i[nbss_offset..];
75d7c9d6 1582
4bf87d30
VJ
1583 self.tc_gap = false;
1584 break;
1585 },
1586 _ => {
1587 let mut consumed = i.len();
1588 if consumed < 4 {
1589 consumed = 0;
1590 } else {
1591 consumed = consumed - 3;
1592 }
1593 SCLogDebug!("smb record NOT found");
1594 return AppLayerResult::incomplete(consumed as u32, 8);
1595 },
1596 }
75d7c9d6
VJ
1597 }
1598 }
1599 while cur_i.len() > 0 { // min record size
1600 match parse_nbss_record(cur_i) {
13b73997 1601 Ok((rem, ref nbss_hdr)) => {
75d7c9d6
VJ
1602 if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1603 // we have the full records size worth of data,
1604 // let's parse it
69cf5c9e 1605 match parse_smb_version(nbss_hdr.data) {
13b73997 1606 Ok((_, ref smb)) => {
75d7c9d6 1607 SCLogDebug!("SMB {:?}", smb);
b3439205 1608 if smb.version == 0xff_u8 { // SMB1
75d7c9d6 1609 SCLogDebug!("SMBv1 record");
69cf5c9e 1610 match parse_smb_record(nbss_hdr.data) {
13b73997 1611 Ok((_, ref smb_record)) => {
75d7c9d6
VJ
1612 smb1_response_record(self, smb_record);
1613 },
1614 _ => {
1615 self.set_event(SMBEvent::MalformedData);
4bf87d30 1616 return AppLayerResult::err();
75d7c9d6
VJ
1617 },
1618 }
b3439205 1619 } else if smb.version == 0xfe_u8 { // SMB2
75d7c9d6
VJ
1620 let mut nbss_data = nbss_hdr.data;
1621 while nbss_data.len() > 0 {
1622 SCLogDebug!("SMBv2 record");
69cf5c9e 1623 match parse_smb2_response_record(nbss_data) {
13b73997 1624 Ok((nbss_data_rem, ref smb_record)) => {
75d7c9d6
VJ
1625 smb2_response_record(self, smb_record);
1626 nbss_data = nbss_data_rem;
b3439205
VJ
1627 },
1628 _ => {
1629 self.set_event(SMBEvent::MalformedData);
4bf87d30 1630 return AppLayerResult::err();
b3439205
VJ
1631 },
1632 }
1633 }
1634 } else if smb.version == 0xfd_u8 { // SMB3 transform
1635 let mut nbss_data = nbss_hdr.data;
1636 while nbss_data.len() > 0 {
1637 SCLogDebug!("SMBv3 transform record");
69cf5c9e 1638 match parse_smb3_transform_record(nbss_data) {
13b73997 1639 Ok((nbss_data_rem, ref _smb3_record)) => {
b3439205 1640 nbss_data = nbss_data_rem;
75d7c9d6
VJ
1641 },
1642 _ => {
1643 self.set_event(SMBEvent::MalformedData);
4bf87d30 1644 return AppLayerResult::err();
75d7c9d6
VJ
1645 },
1646 }
1647 }
1648 }
1649 },
13b73997 1650 Err(nom::Err::Incomplete(_)) => {
75d7c9d6
VJ
1651 // not enough data to contain basic SMB hdr
1652 // TODO event: empty NBSS_MSGTYPE_SESSION_MESSAGE
1653 },
13b73997 1654 Err(_) => {
75d7c9d6 1655 self.set_event(SMBEvent::MalformedData);
4bf87d30 1656 return AppLayerResult::err();
75d7c9d6
VJ
1657 },
1658 }
1659 } else {
1660 SCLogDebug!("NBSS message {:X}", nbss_hdr.message_type);
1661 }
1662 cur_i = rem;
1663 },
13b73997 1664 Err(nom::Err::Incomplete(needed)) => {
7dff9b99 1665 SCLogDebug!("INCOMPLETE have {} needed {:?}", cur_i.len(), needed);
4bf87d30
VJ
1666 if let nom::Needed::Size(n) = needed {
1667 // 512 is the minimum for parse_tcp_data_tc_partial
1668 if n >= 512 && cur_i.len() < 512 {
1669 let total_consumed = i.len() - cur_i.len();
1670 return AppLayerResult::incomplete(total_consumed as u32, 512);
1671 }
1672 let consumed = self.parse_tcp_data_tc_partial(cur_i);
1673 if consumed == 0 {
1674 // if we consumed none we will buffer the entire record
1675 let total_consumed = i.len() - cur_i.len();
1676 SCLogDebug!("setting consumed {} need {} needed {:?} total input {}",
1677 total_consumed, n, needed, i.len());
1678 let need = n + 4; // Incomplete returns size of data minus NBSS header
1679 return AppLayerResult::incomplete(total_consumed as u32, need as u32);
1680 }
1681 // tracking a read record, which we don't need to
1682 // queue up at the stream level, but can feed to us
1683 // in small chunks
1684 return AppLayerResult::ok();
1685 } else {
1686 self.set_event(SMBEvent::InternalError);
1687 return AppLayerResult::err();
1688 }
75d7c9d6 1689 },
13b73997 1690 Err(_) => {
75d7c9d6 1691 self.set_event(SMBEvent::MalformedData);
4bf87d30 1692 return AppLayerResult::err();
75d7c9d6
VJ
1693 },
1694 }
1695 };
89cb3379 1696 self.post_gap_housekeeping(Direction::ToClient);
25f2efe9 1697 if self.check_post_gap_file_txs && !self.post_gap_files_checked {
d41aecce 1698 self.post_gap_housekeeping_for_files();
25f2efe9 1699 self.post_gap_files_checked = true;
d41aecce 1700 }
28f16e38 1701 self._debug_tx_stats();
4bf87d30 1702 AppLayerResult::ok()
75d7c9d6
VJ
1703 }
1704
1705 /// handle a gap in the TOSERVER direction
1706 /// returns: 0 ok, 1 unrecoverable error
5cd9cfb5 1707 pub fn parse_tcp_data_ts_gap(&mut self, gap_size: u32) -> AppLayerResult {
89cb3379 1708 let consumed = self.handle_skip(Direction::ToServer, gap_size);
75d7c9d6
VJ
1709 if consumed < gap_size {
1710 let new_gap_size = gap_size - consumed;
1711 let gap = vec![0; new_gap_size as usize];
1712
89cb3379 1713 let consumed2 = self.filetracker_update(Direction::ToServer, &gap, new_gap_size);
75d7c9d6
VJ
1714 if consumed2 > new_gap_size {
1715 SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size);
1716 self.set_event(SMBEvent::InternalError);
5cd9cfb5 1717 return AppLayerResult::err();
75d7c9d6
VJ
1718 }
1719 }
1720 SCLogDebug!("GAP of size {} in toserver direction", gap_size);
1721 self.ts_ssn_gap = true;
1722 self.ts_gap = true;
5cd9cfb5 1723 return AppLayerResult::ok();
75d7c9d6
VJ
1724 }
1725
1726 /// handle a gap in the TOCLIENT direction
1727 /// returns: 0 ok, 1 unrecoverable error
5cd9cfb5 1728 pub fn parse_tcp_data_tc_gap(&mut self, gap_size: u32) -> AppLayerResult {
89cb3379 1729 let consumed = self.handle_skip(Direction::ToClient, gap_size);
75d7c9d6
VJ
1730 if consumed < gap_size {
1731 let new_gap_size = gap_size - consumed;
1732 let gap = vec![0; new_gap_size as usize];
1733
89cb3379 1734 let consumed2 = self.filetracker_update(Direction::ToClient, &gap, new_gap_size);
75d7c9d6
VJ
1735 if consumed2 > new_gap_size {
1736 SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size);
1737 self.set_event(SMBEvent::InternalError);
5cd9cfb5 1738 return AppLayerResult::err();
75d7c9d6
VJ
1739 }
1740 }
1741 SCLogDebug!("GAP of size {} in toclient direction", gap_size);
1742 self.tc_ssn_gap = true;
1743 self.tc_gap = true;
5cd9cfb5 1744 return AppLayerResult::ok();
75d7c9d6
VJ
1745 }
1746
1747 pub fn trunc_ts(&mut self) {
1748 SCLogDebug!("TRUNC TS");
1749 self.ts_trunc = true;
1750
1751 for tx in &mut self.transactions {
1752 if !tx.request_done {
1753 SCLogDebug!("TRUNCING TX {} in TOSERVER direction", tx.id);
1754 tx.request_done = true;
1755 }
1756 }
1757 }
1758 pub fn trunc_tc(&mut self) {
1759 SCLogDebug!("TRUNC TC");
1760 self.tc_trunc = true;
1761
1762 for tx in &mut self.transactions {
1763 if !tx.response_done {
1764 SCLogDebug!("TRUNCING TX {} in TOCLIENT direction", tx.id);
1765 tx.response_done = true;
1766 }
1767 }
1768 }
1769}
1770
1771/// Returns *mut SMBState
1772#[no_mangle]
547d6c2d 1773pub extern "C" fn rs_smb_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
75d7c9d6
VJ
1774 let state = SMBState::new();
1775 let boxed = Box::new(state);
1776 SCLogDebug!("allocating state");
53413f2d 1777 return Box::into_raw(boxed) as *mut _;
75d7c9d6
VJ
1778}
1779
1780/// Params:
1781/// - state: *mut SMBState as void pointer
1782#[no_mangle]
3f6624bf 1783pub extern "C" fn rs_smb_state_free(state: *mut std::os::raw::c_void) {
75d7c9d6 1784 SCLogDebug!("freeing state");
53413f2d 1785 let mut smb_state = unsafe { Box::from_raw(state as *mut SMBState) };
75d7c9d6
VJ
1786 smb_state.free();
1787}
1788
1789/// C binding parse a SMB request. Returns 1 on success, -1 on failure.
1790#[no_mangle]
363b5f99 1791pub unsafe extern "C" fn rs_smb_parse_request_tcp(flow: *const Flow,
6420df84 1792 state: *mut ffi::c_void,
3f6624bf 1793 _pstate: *mut std::os::raw::c_void,
579cc9f0 1794 input: *const u8,
bf1bd407 1795 input_len: u32,
6420df84 1796 _data: *const std::os::raw::c_void,
2e6014b1 1797 flags: u8)
4bf87d30 1798 -> AppLayerResult
75d7c9d6 1799{
363b5f99 1800 let buf = std::slice::from_raw_parts(input, input_len as usize);
6420df84
SB
1801 let mut state = cast_pointer!(state, SMBState);
1802 let flow = cast_pointer!(flow, Flow);
89cb3379
SB
1803 let file_flags = FileFlowToFlags(flow, Direction::ToServer as u8);
1804 rs_smb_setfileflags(Direction::ToServer as u8, state, file_flags|FILE_USE_DETECT);
75d7c9d6
VJ
1805 SCLogDebug!("parsing {} bytes of request data", input_len);
1806
27af4bb0
SB
1807 if input.is_null() && input_len > 0 {
1808 return rs_smb_parse_request_tcp_gap(state, input_len);
1809 }
2e6014b1
VJ
1810 /* START with MISTREAM set: record might be starting the middle. */
1811 if flags & (STREAM_START|STREAM_MIDSTREAM) == (STREAM_START|STREAM_MIDSTREAM) {
1812 state.ts_gap = true;
1813 }
1814
8aa38060 1815 state.update_ts(flow.get_last_time().as_secs());
4bf87d30 1816 state.parse_tcp_data_ts(buf)
75d7c9d6
VJ
1817}
1818
1819#[no_mangle]
1820pub extern "C" fn rs_smb_parse_request_tcp_gap(
1821 state: &mut SMBState,
bf1bd407 1822 input_len: u32)
5cd9cfb5 1823 -> AppLayerResult
75d7c9d6 1824{
5cd9cfb5 1825 state.parse_tcp_data_ts_gap(input_len as u32)
75d7c9d6
VJ
1826}
1827
1828
1829#[no_mangle]
363b5f99 1830pub unsafe extern "C" fn rs_smb_parse_response_tcp(flow: *const Flow,
6420df84 1831 state: *mut ffi::c_void,
3f6624bf 1832 _pstate: *mut std::os::raw::c_void,
579cc9f0 1833 input: *const u8,
bf1bd407 1834 input_len: u32,
6420df84 1835 _data: *const ffi::c_void,
2e6014b1 1836 flags: u8)
4bf87d30 1837 -> AppLayerResult
75d7c9d6 1838{
6420df84
SB
1839 let mut state = cast_pointer!(state, SMBState);
1840 let flow = cast_pointer!(flow, Flow);
89cb3379
SB
1841 let file_flags = FileFlowToFlags(flow, Direction::ToClient as u8);
1842 rs_smb_setfileflags(Direction::ToClient as u8, state, file_flags|FILE_USE_DETECT);
27af4bb0
SB
1843
1844 if input.is_null() && input_len > 0 {
1845 return rs_smb_parse_response_tcp_gap(state, input_len);
1846 }
75d7c9d6 1847 SCLogDebug!("parsing {} bytes of response data", input_len);
363b5f99 1848 let buf = std::slice::from_raw_parts(input, input_len as usize);
75d7c9d6 1849
2e6014b1
VJ
1850 /* START with MISTREAM set: record might be starting the middle. */
1851 if flags & (STREAM_START|STREAM_MIDSTREAM) == (STREAM_START|STREAM_MIDSTREAM) {
1852 state.tc_gap = true;
1853 }
1854
8aa38060 1855 state.update_ts(flow.get_last_time().as_secs());
4bf87d30 1856 state.parse_tcp_data_tc(buf)
75d7c9d6
VJ
1857}
1858
1859#[no_mangle]
1860pub extern "C" fn rs_smb_parse_response_tcp_gap(
1861 state: &mut SMBState,
bf1bd407 1862 input_len: u32)
5cd9cfb5 1863 -> AppLayerResult
75d7c9d6 1864{
5cd9cfb5 1865 state.parse_tcp_data_tc_gap(input_len as u32)
75d7c9d6
VJ
1866}
1867
89cb3379 1868fn smb_probe_tcp_midstream(direction: Direction, slice: &[u8], rdir: *mut u8) -> i8
75d7c9d6 1869{
17796697 1870 match search_smb_record(slice) {
69cf5c9e 1871 Ok((_, data)) => {
17796697 1872 SCLogDebug!("smb found");
422e4892
VJ
1873 match parse_smb_version(data) {
1874 Ok((_, ref smb)) => {
1875 SCLogDebug!("SMB {:?}", smb);
1876 if smb.version == 0xff_u8 { // SMB1
1877 SCLogDebug!("SMBv1 record");
1878 match parse_smb_record(data) {
1879 Ok((_, ref smb_record)) => {
1880 if smb_record.flags & 0x80 != 0 {
1881 SCLogDebug!("RESPONSE {:02x}", smb_record.flags);
89cb3379
SB
1882 if direction == Direction::ToServer {
1883 unsafe { *rdir = Direction::ToClient as u8; }
422e4892
VJ
1884 }
1885 } else {
1886 SCLogDebug!("REQUEST {:02x}", smb_record.flags);
89cb3379
SB
1887 if direction == Direction::ToClient {
1888 unsafe { *rdir = Direction::ToServer as u8; }
422e4892
VJ
1889 }
1890 }
1891 return 1;
1892 },
1893 _ => { },
1894 }
1895 } else if smb.version == 0xfe_u8 { // SMB2
1896 SCLogDebug!("SMB2 record");
1897 match parse_smb2_record_direction(data) {
1898 Ok((_, ref smb_record)) => {
89cb3379
SB
1899 if direction == Direction::ToServer {
1900 SCLogDebug!("direction Direction::ToServer smb_record {:?}", smb_record);
422e4892 1901 if !smb_record.request {
89cb3379 1902 unsafe { *rdir = Direction::ToClient as u8; }
422e4892
VJ
1903 }
1904 } else {
89cb3379 1905 SCLogDebug!("direction Direction::ToClient smb_record {:?}", smb_record);
422e4892 1906 if smb_record.request {
89cb3379 1907 unsafe { *rdir = Direction::ToServer as u8; }
422e4892
VJ
1908 }
1909 }
1910 },
1911 _ => {},
1912 }
1913 }
1914 else if smb.version == 0xfd_u8 { // SMB3 transform
1915 SCLogDebug!("SMB3 record");
1916 }
1917 return 1;
1918 },
1919 _ => {
1920 SCLogDebug!("smb not found in {:?}", slice);
1921 },
1922 }
17796697
VJ
1923 },
1924 _ => {
422e4892 1925 SCLogDebug!("no dice");
17796697
VJ
1926 },
1927 }
9dc5258a
PA
1928 return 0;
1929}
1930
1931// probing parser
1932// return 1 if found, 0 is not found
1933#[no_mangle]
363b5f99 1934pub unsafe extern "C" fn rs_smb_probe_tcp(_f: *const Flow,
6420df84
SB
1935 flags: u8, input: *const u8, len: u32, rdir: *mut u8)
1936 -> AppProto
9dc5258a 1937{
27af4bb0
SB
1938 if len < MIN_REC_SIZE as u32 {
1939 return ALPROTO_UNKNOWN;
1940 }
9dc5258a 1941 let slice = build_slice!(input, len as usize);
1b6e81cd 1942 if flags & STREAM_MIDSTREAM == STREAM_MIDSTREAM {
89cb3379 1943 if smb_probe_tcp_midstream(flags.into(), slice, rdir) == 1 {
363b5f99 1944 return ALPROTO_SMB;
1b6e81cd 1945 }
9dc5258a 1946 }
7ab071a5 1947 match parse_nbss_record_partial(slice) {
13b73997 1948 Ok((_, ref hdr)) => {
7ab071a5 1949 if hdr.is_smb() {
17796697 1950 SCLogDebug!("smb found");
363b5f99 1951 return ALPROTO_SMB;
61814590
PA
1952 } else if hdr.needs_more(){
1953 return 0;
83070102
PA
1954 } else if hdr.is_valid() &&
1955 hdr.message_type != NBSS_MSGTYPE_SESSION_MESSAGE {
1956 //we accept a first small netbios message before real SMB
1957 let hl = hdr.length as usize;
1958 if hdr.data.len() >= hl + 8 {
1959 // 8 is 4 bytes NBSS + 4 bytes SMB0xFX magic
1960 match parse_nbss_record_partial(&hdr.data[hl..]) {
1961 Ok((_, ref hdr2)) => {
1962 if hdr2.is_smb() {
1963 SCLogDebug!("smb found");
363b5f99 1964 return ALPROTO_SMB;
83070102
PA
1965 }
1966 }
1967 _ => {}
1968 }
1969 } else if hdr.length < 256 {
1970 // we want more data, 256 is some random value
1971 return 0;
1972 }
1973 // default is failure
7ab071a5
VJ
1974 }
1975 },
1976 _ => { },
1977 }
17796697 1978 SCLogDebug!("no smb");
363b5f99 1979 return ALPROTO_FAILED;
75d7c9d6
VJ
1980}
1981
1982#[no_mangle]
363b5f99 1983pub unsafe extern "C" fn rs_smb_state_get_tx_count(state: *mut ffi::c_void)
bf1bd407 1984 -> u64
75d7c9d6 1985{
6420df84 1986 let state = cast_pointer!(state, SMBState);
75d7c9d6
VJ
1987 SCLogDebug!("rs_smb_state_get_tx_count: returning {}", state.tx_id);
1988 return state.tx_id;
1989}
1990
1991#[no_mangle]
363b5f99 1992pub unsafe extern "C" fn rs_smb_state_get_tx(state: *mut ffi::c_void,
bf1bd407 1993 tx_id: u64)
6420df84 1994 -> *mut ffi::c_void
75d7c9d6 1995{
6420df84 1996 let state = cast_pointer!(state, SMBState);
75d7c9d6
VJ
1997 match state.get_tx_by_id(tx_id) {
1998 Some(tx) => {
53413f2d 1999 return tx as *const _ as *mut _;
75d7c9d6
VJ
2000 }
2001 None => {
2002 return std::ptr::null_mut();
2003 }
2004 }
2005}
2006
75d7c9d6 2007#[no_mangle]
363b5f99 2008pub unsafe extern "C" fn rs_smb_state_tx_free(state: *mut ffi::c_void,
bf1bd407 2009 tx_id: u64)
75d7c9d6 2010{
6420df84 2011 let state = cast_pointer!(state, SMBState);
75d7c9d6
VJ
2012 SCLogDebug!("freeing tx {}", tx_id as u64);
2013 state.free_tx(tx_id);
2014}
2015
75d7c9d6 2016#[no_mangle]
363b5f99 2017pub unsafe extern "C" fn rs_smb_tx_get_alstate_progress(tx: *mut ffi::c_void,
bf1bd407 2018 direction: u8)
6420df84 2019 -> i32
75d7c9d6 2020{
6420df84
SB
2021 let tx = cast_pointer!(tx, SMBTransaction);
2022
89cb3379 2023 if direction == Direction::ToServer as u8 && tx.request_done {
75d7c9d6
VJ
2024 SCLogDebug!("tx {} TOSERVER progress 1 => {:?}", tx.id, tx);
2025 return 1;
89cb3379 2026 } else if direction == Direction::ToClient as u8 && tx.response_done {
75d7c9d6
VJ
2027 SCLogDebug!("tx {} TOCLIENT progress 1 => {:?}", tx.id, tx);
2028 return 1;
2029 } else {
89cb3379 2030 SCLogDebug!("tx {} direction {:?} progress 0", tx.id, direction);
75d7c9d6
VJ
2031 return 0;
2032 }
2033}
2034
6420df84 2035
75d7c9d6 2036#[no_mangle]
363b5f99 2037pub unsafe extern "C" fn rs_smb_get_tx_data(
77a95edd
VJ
2038 tx: *mut std::os::raw::c_void)
2039 -> *mut AppLayerTxData
75d7c9d6 2040{
77a95edd
VJ
2041 let tx = cast_pointer!(tx, SMBTransaction);
2042 return &mut tx.tx_data;
75d7c9d6
VJ
2043}
2044
6420df84 2045
75d7c9d6 2046#[no_mangle]
363b5f99 2047pub unsafe extern "C" fn rs_smb_state_truncate(
6420df84 2048 state: *mut std::ffi::c_void,
bf1bd407 2049 direction: u8)
75d7c9d6 2050{
6420df84 2051 let state = cast_pointer!(state, SMBState);
89cb3379
SB
2052 match direction.into() {
2053 Direction::ToServer => {
2054 state.trunc_ts();
2055 }
2056 Direction::ToClient => {
2057 state.trunc_tc();
2058 }
75d7c9d6
VJ
2059 }
2060}
2061
9ccc28ba 2062#[no_mangle]
b9f10ba2
JI
2063pub unsafe extern "C" fn rs_smb_state_get_event_info_by_id(
2064 event_id: std::os::raw::c_int,
2065 event_name: *mut *const std::os::raw::c_char,
2066 event_type: *mut AppLayerEventType,
2067) -> i8 {
2068 SMBEvent::get_event_info_by_id(event_id, event_name, event_type)
9ccc28ba
JL
2069}
2070
75d7c9d6 2071#[no_mangle]
b9f10ba2
JI
2072pub unsafe extern "C" fn rs_smb_state_get_event_info(
2073 event_name: *const std::os::raw::c_char,
2074 event_id: *mut std::os::raw::c_int,
2075 event_type: *mut AppLayerEventType,
2076) -> std::os::raw::c_int {
2077 SMBEvent::get_event_info(event_name, event_id, event_type)
75d7c9d6 2078}
e5c948df 2079
363b5f99 2080pub unsafe extern "C" fn smb3_probe_tcp(f: *const Flow, dir: u8, input: *const u8, len: u32, rdir: *mut u8) -> u16 {
e5c948df
SB
2081 let retval = rs_smb_probe_tcp(f, dir, input, len, rdir);
2082 let f = cast_pointer!(f, Flow);
363b5f99 2083 if retval != ALPROTO_SMB {
aa9d8658
VJ
2084 return retval;
2085 }
2086 let (sp, dp) = f.get_ports();
2087 let flags = f.get_flags();
2088 let fsp = if (flags & FLOW_DIR_REVERSED) != 0 { dp } else { sp };
2089 let fdp = if (flags & FLOW_DIR_REVERSED) != 0 { sp } else { dp };
2090 if fsp == 445 && fdp != 445 {
89cb3379
SB
2091 match dir.into() {
2092 Direction::ToServer => {
2093 *rdir = Direction::ToClient as u8;
2094 }
2095 Direction::ToClient => {
2096 *rdir = Direction::ToServer as u8;
2097 }
e5c948df 2098 }
aa9d8658 2099 }
363b5f99 2100 return ALPROTO_SMB;
e5c948df
SB
2101}
2102
2103fn register_pattern_probe() -> i8 {
2104 let mut r = 0;
2105 unsafe {
2106 // SMB1
2107 r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP as u8, ALPROTO_SMB,
2108 b"|ff|SMB\0".as_ptr() as *const std::os::raw::c_char, 8, 4,
89cb3379 2109 Direction::ToServer as u8, rs_smb_probe_tcp, MIN_REC_SIZE, MIN_REC_SIZE);
e5c948df
SB
2110 r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP as u8, ALPROTO_SMB,
2111 b"|ff|SMB\0".as_ptr() as *const std::os::raw::c_char, 8, 4,
89cb3379 2112 Direction::ToClient as u8, rs_smb_probe_tcp, MIN_REC_SIZE, MIN_REC_SIZE);
e5c948df
SB
2113 // SMB2/3
2114 r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP as u8, ALPROTO_SMB,
2115 b"|fe|SMB\0".as_ptr() as *const std::os::raw::c_char, 8, 4,
89cb3379 2116 Direction::ToServer as u8, rs_smb_probe_tcp, MIN_REC_SIZE, MIN_REC_SIZE);
e5c948df
SB
2117 r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP as u8, ALPROTO_SMB,
2118 b"|fe|SMB\0".as_ptr() as *const std::os::raw::c_char, 8, 4,
89cb3379 2119 Direction::ToClient as u8, rs_smb_probe_tcp, MIN_REC_SIZE, MIN_REC_SIZE);
e5c948df
SB
2120 // SMB3 encrypted records
2121 r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP as u8, ALPROTO_SMB,
2122 b"|fd|SMB\0".as_ptr() as *const std::os::raw::c_char, 8, 4,
89cb3379 2123 Direction::ToServer as u8, smb3_probe_tcp, MIN_REC_SIZE, MIN_REC_SIZE);
e5c948df
SB
2124 r |= AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP as u8, ALPROTO_SMB,
2125 b"|fd|SMB\0".as_ptr() as *const std::os::raw::c_char, 8, 4,
89cb3379 2126 Direction::ToClient as u8, smb3_probe_tcp, MIN_REC_SIZE, MIN_REC_SIZE);
e5c948df
SB
2127 }
2128
2129 if r == 0 {
2130 return 0;
2131 } else {
2132 return -1;
2133 }
2134}
2135
2136// Parser name as a C style string.
2137const PARSER_NAME: &'static [u8] = b"smb\0";
2138
2139#[no_mangle]
2140pub unsafe extern "C" fn rs_smb_register_parser() {
2141 let default_port = CString::new("445").unwrap();
2142 let mut stream_depth = SMB_CONFIG_DEFAULT_STREAM_DEPTH;
2143 let parser = RustParser {
2144 name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
1f48714e 2145 default_port: std::ptr::null(),
e5c948df
SB
2146 ipproto: IPPROTO_TCP,
2147 probe_ts: None,
2148 probe_tc: None,
2149 min_depth: 0,
2150 max_depth: 16,
2151 state_new: rs_smb_state_new,
2152 state_free: rs_smb_state_free,
2153 tx_free: rs_smb_state_tx_free,
2154 parse_ts: rs_smb_parse_request_tcp,
2155 parse_tc: rs_smb_parse_response_tcp,
2156 get_tx_count: rs_smb_state_get_tx_count,
2157 get_tx: rs_smb_state_get_tx,
2158 tx_comp_st_ts: 1,
2159 tx_comp_st_tc: 1,
2160 tx_get_progress: rs_smb_tx_get_alstate_progress,
e5c948df
SB
2161 get_eventinfo: Some(rs_smb_state_get_event_info),
2162 get_eventinfo_byid : Some(rs_smb_state_get_event_info_by_id),
2163 localstorage_new: None,
2164 localstorage_free: None,
2165 get_files: Some(rs_smb_getfiles),
fcfc9876 2166 get_tx_iterator: Some(applayer::state_get_tx_iterator::<SMBState, SMBTransaction>),
e5c948df
SB
2167 get_tx_data: rs_smb_get_tx_data,
2168 apply_tx_config: None,
2169 flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS,
2170 truncate: Some(rs_smb_state_truncate),
2171 };
2172
2173 let ip_proto_str = CString::new("tcp").unwrap();
2174
2175 if AppLayerProtoDetectConfProtoDetectionEnabled(
2176 ip_proto_str.as_ptr(),
2177 parser.name,
2178 ) != 0
2179 {
2180 let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
2181 ALPROTO_SMB = alproto;
2182 if register_pattern_probe() < 0 {
2183 return;
2184 }
2185
2186 let have_cfg = AppLayerProtoDetectPPParseConfPorts(ip_proto_str.as_ptr(),
2187 IPPROTO_TCP as u8, parser.name, ALPROTO_SMB, 0,
2188 MIN_REC_SIZE, rs_smb_probe_tcp, rs_smb_probe_tcp);
2189
2190 if have_cfg == 0 {
1f48714e 2191 AppLayerProtoDetectPPRegister(IPPROTO_TCP as u8, default_port.as_ptr(), ALPROTO_SMB,
89cb3379 2192 0, MIN_REC_SIZE, Direction::ToServer as u8, rs_smb_probe_tcp, rs_smb_probe_tcp);
e5c948df
SB
2193 }
2194
2195 if AppLayerParserConfParserEnabled(
2196 ip_proto_str.as_ptr(),
2197 parser.name,
2198 ) != 0
2199 {
2200 let _ = AppLayerRegisterParser(&parser, alproto);
2201 }
2202 SCLogDebug!("Rust SMB parser registered.");
2203 let retval = conf_get("app-layer.protocols.smb.stream-depth");
2204 if let Some(val) = retval {
42da0fb5
SB
2205 match get_memval(val) {
2206 Ok(retval) => { stream_depth = retval as u32; }
2207 Err(_) => { SCLogError!("Invalid depth value"); }
e5c948df
SB
2208 }
2209 AppLayerParserSetStreamDepth(IPPROTO_TCP as u8, ALPROTO_SMB, stream_depth);
2210 }
2211 } else {
2212 SCLogDebug!("Protocol detector and parser disabled for SMB.");
2213 }
2214}