]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/smb/smb.rs
smb: add smb records to fileinfo
[people/ms/suricata.git] / rust / src / smb / smb.rs
CommitLineData
75d7c9d6
VJ
1/* Copyright (C) 2017 Open Information Security Foundation
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
27extern crate libc;
28use std;
29use std::mem::transmute;
30use std::str;
31use std::ffi::CStr;
32
33use nom::IResult;
34use std::collections::HashMap;
35
36use core::*;
37use log::*;
38use applayer;
39use applayer::LoggerFlags;
40
41use smb::nbss_records::*;
42use smb::smb1_records::*;
43use smb::smb2_records::*;
44
45use smb::smb1::*;
46use smb::smb2::*;
b3439205 47use smb::smb3::*;
75d7c9d6 48use smb::dcerpc::*;
8bef1208 49use smb::session::*;
75d7c9d6 50use smb::events::*;
75d7c9d6 51use smb::files::*;
283be3ca 52use smb::smb2_ioctl::*;
75d7c9d6
VJ
53
54pub static mut SURICATA_SMB_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
55
56#[no_mangle]
57pub extern "C" fn rs_smb_init(context: &'static mut SuricataFileContext)
58{
59 unsafe {
60 SURICATA_SMB_FILE_CONFIG = Some(context);
61 }
62}
63
75d7c9d6
VJ
64pub const SMB_NTSTATUS_SUCCESS: u32 = 0;
65pub const SMB_NTSTATUS_PENDING: u32 = 0x00000103;
66pub const SMB_NTSTATUS_BUFFER_OVERFLOW: u32 = 0x80000005;
67pub const SMB_NTSTATUS_NO_MORE_FILES: u32 = 0x80000006;
68pub const SMB_NTSTATUS_NO_MORE_ENTRIES: u32 = 0x8000001a;
69pub const SMB_NTSTATUS_INVALID_HANDLE: u32 = 0xc0000008;
70pub const SMB_NTSTATUS_INVALID_PARAMETER: u32 = 0xc000000d;
71pub const SMB_NTSTATUS_NO_SUCH_DEVICE: u32 = 0xc000000e;
72pub const SMB_NTSTATUS_NO_SUCH_FILE: u32 = 0xc000000f;
73pub const SMB_NTSTATUS_INVALID_DEVICE_REQUEST: u32 = 0xc0000010;
74pub const SMB_NTSTATUS_END_OF_FILE: u32 = 0xc0000011;
75pub const SMB_NTSTATUS_MORE_PROCESSING_REQUIRED: u32 = 0xc0000016;
76pub const SMB_NTSTATUS_ACCESS_DENIED: u32 = 0xc0000022;
77pub const SMB_NTSTATUS_OBJECT_NAME_INVALID: u32 = 0xc0000033;
78pub const SMB_NTSTATUS_OBJECT_NAME_NOT_FOUND: u32 = 0xc0000034;
79pub const SMB_NTSTATUS_OBJECT_NAME_COLLISION: u32 = 0xc0000035;
80pub const SMB_NTSTATUS_OBJECT_PATH_NOT_FOUND: u32 = 0xc000003a;
81pub const SMB_NTSTATUS_SHARING_VIOLATION: u32 = 0xc0000043;
82pub const SMB_NTSTATUS_LOCK_CONFLICT: u32 = 0xc0000054;
83pub const SMB_NTSTATUS_LOCK_NOT_GRANTED: u32 = 0xc0000055;
84pub const SMB_NTSTATUS_PRIVILEGE_NOT_HELD: u32 = 0xc0000061;
85pub const SMB_NTSTATUS_LOGON_FAILURE: u32 = 0xc000006d;
86pub const SMB_NTSTATUS_PIPE_DISCONNECTED: u32 = 0xc00000b0;
87pub const SMB_NTSTATUS_FILE_IS_A_DIRECTORY: u32 = 0xc00000ba;
88pub const SMB_NTSTATUS_NOT_SUPPORTED: u32 = 0xc00000bb;
89pub const SMB_NTSTATUS_BAD_NETWORK_NAME: u32 = 0xc00000cc;
7ceb6713 90pub const SMB_NTSTATUS_REQUEST_NOT_ACCEPTED: u32 = 0xc00000d0;
75d7c9d6
VJ
91pub const SMB_NTSTATUS_OPLOCK_NOT_GRANTED: u32 = 0xc00000e2;
92pub const SMB_NTSTATUS_CANCELLED: u32 = 0xc0000120;
93pub const SMB_NTSTATUS_FILE_CLOSED: u32 = 0xc0000128;
94pub const SMB_NTSTATUS_FS_DRIVER_REQUIRED: u32 = 0xc000019c;
95pub const SMB_NTSTATUS_INSUFF_SERVER_RESOURCES: u32 = 0xc0000205;
96pub const SMB_NTSTATUS_NOT_FOUND: u32 = 0xc0000225;
97pub const SMB_NTSTATUS_PIPE_BROKEN: u32 = 0xc000014b;
98pub const SMB_NTSTATUS_TRUSTED_RELATIONSHIP_FAILURE: u32 = 0xc000018d;
99pub const SMB_NTSTATUS_NOT_A_REPARSE_POINT: u32 = 0xc0000275;
100pub const SMB_NTSTATUS_NETWORK_SESSION_EXPIRED: u32 = 0xc000035c;
101
102pub fn smb_ntstatus_string(c: u32) -> String {
103 match c {
104 SMB_NTSTATUS_SUCCESS => "STATUS_SUCCESS",
105 SMB_NTSTATUS_BUFFER_OVERFLOW => "STATUS_BUFFER_OVERFLOW",
106 SMB_NTSTATUS_PENDING => "STATUS_PENDING",
107 SMB_NTSTATUS_NO_MORE_FILES => "STATUS_NO_MORE_FILES",
108 SMB_NTSTATUS_NO_MORE_ENTRIES => "STATUS_NO_MORE_ENTRIES",
109 SMB_NTSTATUS_INVALID_HANDLE => "STATUS_INVALID_HANDLE",
110 SMB_NTSTATUS_INVALID_PARAMETER => "STATUS_INVALID_PARAMETER",
111 SMB_NTSTATUS_NO_SUCH_DEVICE => "STATUS_NO_SUCH_DEVICE",
112 SMB_NTSTATUS_NO_SUCH_FILE => "STATUS_NO_SUCH_FILE",
113 SMB_NTSTATUS_INVALID_DEVICE_REQUEST => "STATUS_INVALID_DEVICE_REQUEST",
114 SMB_NTSTATUS_END_OF_FILE => "STATUS_END_OF_FILE",
115 SMB_NTSTATUS_MORE_PROCESSING_REQUIRED => "STATUS_MORE_PROCESSING_REQUIRED",
116 SMB_NTSTATUS_ACCESS_DENIED => "STATUS_ACCESS_DENIED",
117 SMB_NTSTATUS_OBJECT_NAME_INVALID => "STATUS_OBJECT_NAME_INVALID",
118 SMB_NTSTATUS_OBJECT_NAME_NOT_FOUND => "STATUS_OBJECT_NAME_NOT_FOUND",
119 SMB_NTSTATUS_OBJECT_NAME_COLLISION => "STATUS_OBJECT_NAME_COLLISION",
120 SMB_NTSTATUS_OBJECT_PATH_NOT_FOUND => "STATUS_OBJECT_PATH_NOT_FOUND",
121 SMB_NTSTATUS_SHARING_VIOLATION => "STATUS_SHARING_VIOLATION",
122 SMB_NTSTATUS_LOCK_CONFLICT => "STATUS_LOCK_CONFLICT",
123 SMB_NTSTATUS_LOCK_NOT_GRANTED => "STATUS_LOCK_NOT_GRANTED",
124 SMB_NTSTATUS_PRIVILEGE_NOT_HELD => "STATUS_PRIVILEGE_NOT_HELD",
125 SMB_NTSTATUS_LOGON_FAILURE => "STATUS_LOGON_FAILURE",
126 SMB_NTSTATUS_PIPE_DISCONNECTED => "STATUS_PIPE_DISCONNECTED",
127 SMB_NTSTATUS_FILE_IS_A_DIRECTORY => "STATUS_FILE_IS_A_DIRECTORY",
128 SMB_NTSTATUS_NOT_SUPPORTED => "STATUS_NOT_SUPPORTED",
129 SMB_NTSTATUS_BAD_NETWORK_NAME => "STATUS_BAD_NETWORK_NAME",
7ceb6713 130 SMB_NTSTATUS_REQUEST_NOT_ACCEPTED => "STATUS_REQUEST_NOT_ACCEPTED",
75d7c9d6
VJ
131 SMB_NTSTATUS_OPLOCK_NOT_GRANTED => "STATUS_OPLOCK_NOT_GRANTED",
132 SMB_NTSTATUS_CANCELLED => "STATUS_CANCELLED",
133 SMB_NTSTATUS_FILE_CLOSED => "STATUS_FILE_CLOSED",
134 SMB_NTSTATUS_FS_DRIVER_REQUIRED => "STATUS_FS_DRIVER_REQUIRED",
135 SMB_NTSTATUS_INSUFF_SERVER_RESOURCES => "STATUS_INSUFF_SERVER_RESOURCES",
136 SMB_NTSTATUS_NOT_FOUND => "STATUS_NOT_FOUND",
137 SMB_NTSTATUS_PIPE_BROKEN => "STATUS_PIPE_BROKEN",
138 SMB_NTSTATUS_TRUSTED_RELATIONSHIP_FAILURE => "STATUS_TRUSTED_RELATIONSHIP_FAILURE",
139 SMB_NTSTATUS_NOT_A_REPARSE_POINT => "STATUS_NOT_A_REPARSE_POINT",
140 SMB_NTSTATUS_NETWORK_SESSION_EXPIRED => "STATUS_NETWORK_SESSION_EXPIRED",
141 _ => { return (c).to_string(); },
142 }.to_string()
143}
144
816bd022
VJ
145pub const SMB_SRV_ERROR: u16 = 1;
146pub const SMB_SRV_BADPW: u16 = 2;
147pub const SMB_SRV_BADTYPE: u16 = 3;
148pub const SMB_SRV_ACCESS: u16 = 4;
149pub const SMB_SRV_BADUID: u16 = 91;
150
151pub fn smb_srv_error_string(c: u16) -> String {
152 match c {
153 SMB_SRV_ERROR => "SRV_ERROR",
154 SMB_SRV_BADPW => "SRV_BADPW",
155 SMB_SRV_BADTYPE => "SRV_BADTYPE",
156 SMB_SRV_ACCESS => "SRV_ACCESS",
157 SMB_SRV_BADUID => "SRV_BADUID",
158 _ => { return (c).to_string(); },
159 }.to_string()
160}
75d7c9d6
VJ
161
162pub const SMB_DOS_SUCCESS: u16 = 0;
163pub const SMB_DOS_BAD_FUNC: u16 = 1;
164pub const SMB_DOS_BAD_FILE: u16 = 2;
165pub const SMB_DOS_BAD_PATH: u16 = 3;
166pub const SMB_DOS_TOO_MANY_OPEN_FILES: u16 = 4;
167pub const SMB_DOS_ACCESS_DENIED: u16 = 5;
168
169pub fn smb_dos_error_string(c: u16) -> String {
170 match c {
171 SMB_DOS_SUCCESS => "DOS_SUCCESS",
172 SMB_DOS_BAD_FUNC => "DOS_BAD_FUNC",
173 SMB_DOS_BAD_FILE => "DOS_BAD_FILE",
174 SMB_DOS_BAD_PATH => "DOS_BAD_PATH",
175 SMB_DOS_TOO_MANY_OPEN_FILES => "DOS_TOO_MANY_OPEN_FILES",
176 SMB_DOS_ACCESS_DENIED => "DOS_ACCESS_DENIED",
177 _ => { return (c).to_string(); },
178 }.to_string()
179}
180
181pub const NTLMSSP_NEGOTIATE: u32 = 1;
182pub const NTLMSSP_CHALLENGE: u32 = 2;
183pub const NTLMSSP_AUTH: u32 = 3;
184
185pub fn ntlmssp_type_string(c: u32) -> String {
186 match c {
187 NTLMSSP_NEGOTIATE => "NTLMSSP_NEGOTIATE",
188 NTLMSSP_CHALLENGE => "NTLMSSP_CHALLENGE",
189 NTLMSSP_AUTH => "NTLMSSP_AUTH",
190 _ => { return (c).to_string(); },
191 }.to_string()
192}
193
194#[derive(Eq, PartialEq, Debug, Clone)]
195pub struct SMBVerCmdStat {
196 smb_ver: u8,
197 smb1_cmd: u8,
198 smb2_cmd: u16,
199
200 status_set: bool,
201 status_is_dos_error: bool,
816bd022 202 status_error_class: u8,
75d7c9d6
VJ
203 status: u32,
204}
205
206impl SMBVerCmdStat {
207 pub fn new() -> SMBVerCmdStat {
208 return SMBVerCmdStat {
209 smb_ver: 0,
210 smb1_cmd: 0,
211 smb2_cmd: 0,
212 status_set: false,
213 status_is_dos_error: false,
816bd022 214 status_error_class: 0,
75d7c9d6
VJ
215 status: 0,
216 }
217 }
218 pub fn new1(cmd: u8) -> SMBVerCmdStat {
219 return SMBVerCmdStat {
220 smb_ver: 1,
221 smb1_cmd: cmd,
222 smb2_cmd: 0,
223 status_set: false,
224 status_is_dos_error: false,
816bd022 225 status_error_class: 0,
75d7c9d6
VJ
226 status: 0,
227 }
228 }
229 pub fn new1_with_ntstatus(cmd: u8, status: u32) -> SMBVerCmdStat {
230 return SMBVerCmdStat {
231 smb_ver: 1,
232 smb1_cmd: cmd,
233 smb2_cmd: 0,
234 status_set: true,
235 status_is_dos_error: false,
816bd022 236 status_error_class: 0,
75d7c9d6
VJ
237 status: status,
238 }
239 }
240 pub fn new2(cmd: u16) -> SMBVerCmdStat {
241 return SMBVerCmdStat {
242 smb_ver: 2,
243 smb1_cmd: 0,
244 smb2_cmd: cmd,
245 status_set: false,
246 status_is_dos_error: false,
816bd022 247 status_error_class: 0,
75d7c9d6
VJ
248 status: 0,
249 }
250 }
251
252 pub fn new2_with_ntstatus(cmd: u16, status: u32) -> SMBVerCmdStat {
253 return SMBVerCmdStat {
254 smb_ver: 2,
255 smb1_cmd: 0,
256 smb2_cmd: cmd,
257 status_set: true,
258 status_is_dos_error: false,
816bd022 259 status_error_class: 0,
75d7c9d6
VJ
260 status: status,
261 }
262 }
263
264 pub fn set_smb1_cmd(&mut self, cmd: u8) -> bool {
265 if self.smb_ver != 0 {
266 return false;
267 }
268 self.smb_ver = 1;
269 self.smb1_cmd = cmd;
270 return true;
271 }
272
273 pub fn set_smb2_cmd(&mut self, cmd: u16) -> bool {
274 if self.smb_ver != 0 {
275 return false;
276 }
277 self.smb_ver = 2;
278 self.smb2_cmd = cmd;
279 return true;
280 }
281
282 pub fn get_version(&self) -> u8 {
283 self.smb_ver
284 }
285
286 pub fn get_smb1_cmd(&self) -> (bool, u8) {
287 if self.smb_ver != 1 {
288 return (false, 0);
289 }
290 return (true, self.smb1_cmd);
291 }
292
293 pub fn get_smb2_cmd(&self) -> (bool, u16) {
294 if self.smb_ver != 2 {
295 return (false, 0);
296 }
297 return (true, self.smb2_cmd);
298 }
299
300 pub fn get_ntstatus(&self) -> (bool, u32) {
301 (self.status_set && !self.status_is_dos_error, self.status)
302 }
303
816bd022
VJ
304 pub fn get_dos_error(&self) -> (bool, u8, u16) {
305 (self.status_set && self.status_is_dos_error, self.status_error_class, self.status as u16)
75d7c9d6
VJ
306 }
307
308 fn set_status(&mut self, status: u32, is_dos_error: bool)
309 {
310 if is_dos_error {
311 self.status_is_dos_error = true;
816bd022 312 self.status_error_class = (status & 0x0000_00ff) as u8;
75d7c9d6
VJ
313 self.status = (status & 0xffff_0000) >> 16;
314 } else {
315 self.status = status;
316 }
317 self.status_set = true;
318 }
319
320 pub fn set_ntstatus(&mut self, status: u32)
321 {
322 self.set_status(status, false)
323 }
324
325 pub fn set_status_dos_error(&mut self, status: u32)
326 {
327 self.set_status(status, true)
328 }
329}
330
0e05ef73
VJ
331/// "The FILETIME structure is a 64-bit value that represents the number of
332/// 100-nanosecond intervals that have elapsed since January 1, 1601,
333/// Coordinated Universal Time (UTC)."
334#[derive(Eq, PartialEq, Debug, Clone)]
335pub struct SMBFiletime {
336 ts: u64,
337}
338
339impl SMBFiletime {
340 pub fn new(raw: u64) -> SMBFiletime {
341 SMBFiletime {
342 ts: raw,
343 }
344 }
345
346 /// inspired by Bro, convert FILETIME to secs since unix epoch
347 pub fn as_unix(&self) -> u32 {
348 if self.ts > 116_444_736_000_000_000_u64 {
349 let ts = self.ts / 10000000 - 11644473600;
350 ts as u32
351 } else {
352 0
353 }
354 }
355}
356
75d7c9d6
VJ
357#[derive(Debug)]
358pub enum SMBTransactionTypeData {
359 FILE(SMBTransactionFile),
360 TREECONNECT(SMBTransactionTreeConnect),
361 NEGOTIATE(SMBTransactionNegotiate),
362 DCERPC(SMBTransactionDCERPC),
363 CREATE(SMBTransactionCreate),
8bef1208 364 SESSIONSETUP(SMBTransactionSessionSetup),
5c260207 365 IOCTL(SMBTransactionIoctl),
75d7c9d6
VJ
366}
367
368#[derive(Debug)]
369pub struct SMBTransactionCreate {
370 pub disposition: u32,
371 pub delete_on_close: bool,
372 pub directory: bool,
373 pub filename: Vec<u8>,
0e05ef73
VJ
374
375 pub create_ts: u32,
376 pub last_access_ts: u32,
377 pub last_write_ts: u32,
378 pub last_change_ts: u32,
379
380 pub size: u64,
75d7c9d6
VJ
381}
382
383impl SMBTransactionCreate {
384 pub fn new(filename: Vec<u8>, disp: u32, del: bool, dir: bool) -> SMBTransactionCreate {
385 return SMBTransactionCreate {
386 disposition: disp,
387 delete_on_close: del,
388 directory: dir,
389 filename: filename,
0e05ef73
VJ
390 create_ts: 0,
391 last_access_ts: 0,
392 last_write_ts: 0,
393 last_change_ts: 0,
394 size: 0,
75d7c9d6
VJ
395 }
396 }
397}
398
399#[derive(Debug)]
400pub struct SMBTransactionNegotiate {
401 pub smb_ver: u8,
402 pub dialects: Vec<Vec<u8>>,
403 pub dialects2: Vec<Vec<u8>>,
6d56edc3
VJ
404
405 // SMB1 doesn't have the client GUID
406 pub client_guid: Option<Vec<u8>>,
407 pub server_guid: Vec<u8>,
75d7c9d6
VJ
408}
409
410impl SMBTransactionNegotiate {
411 pub fn new(smb_ver: u8) -> SMBTransactionNegotiate {
412 return SMBTransactionNegotiate {
413 smb_ver: smb_ver,
414 dialects: Vec::new(),
415 dialects2: Vec::new(),
6d56edc3
VJ
416 client_guid: None,
417 server_guid: Vec::with_capacity(16),
75d7c9d6
VJ
418 }
419 }
420}
421
422#[derive(Debug)]
423pub struct SMBTransactionTreeConnect {
424 pub is_pipe: bool,
c56f5e11 425 pub share_type: u8,
75d7c9d6
VJ
426 pub tree_id: u32,
427 pub share_name: Vec<u8>,
fcbeab70
VJ
428
429 /// SMB1 service strings
430 pub req_service: Option<Vec<u8>>,
431 pub res_service: Option<Vec<u8>>,
75d7c9d6
VJ
432}
433
434impl SMBTransactionTreeConnect {
435 pub fn new(share_name: Vec<u8>) -> SMBTransactionTreeConnect {
436 return SMBTransactionTreeConnect {
437 is_pipe:false,
c56f5e11 438 share_type: 0,
75d7c9d6
VJ
439 tree_id:0,
440 share_name:share_name,
fcbeab70
VJ
441 req_service: None,
442 res_service: None,
75d7c9d6
VJ
443 }
444 }
445}
446
447#[derive(Debug)]
448pub struct SMBTransaction {
449 pub id: u64, /// internal id
450
451 /// version, command and status
452 pub vercmd: SMBVerCmdStat,
453 /// session id, tree id, etc.
454 pub hdr: SMBCommonHdr,
455
456 /// for state tracking. false means this side is in progress, true
457 /// that it's complete.
458 pub request_done: bool,
459 pub response_done: bool,
460
461 /// Command specific data
462 pub type_data: Option<SMBTransactionTypeData>,
463
464 /// detection engine flags for use by detection engine
465 detect_flags_ts: u64,
466 detect_flags_tc: u64,
467 pub logged: LoggerFlags,
468 pub de_state: Option<*mut DetectEngineState>,
469 pub events: *mut AppLayerDecoderEvents,
470}
471
472impl SMBTransaction {
473 pub fn new() -> SMBTransaction {
474 return SMBTransaction{
475 id: 0,
476 vercmd: SMBVerCmdStat::new(),
477 hdr: SMBCommonHdr::init(),
478 request_done: false,
479 response_done: false,
480 type_data: None,
481 detect_flags_ts: 0,
482 detect_flags_tc: 0,
483 logged: LoggerFlags::new(),
484 de_state: None,
485 events: std::ptr::null_mut(),
486 }
487 }
488
489 pub fn set_status(&mut self, status: u32, is_dos_error: bool)
490 {
491 if is_dos_error {
492 self.vercmd.set_status_dos_error(status);
493 } else {
494 self.vercmd.set_ntstatus(status);
495 }
496 }
497
498 pub fn free(&mut self) {
499 if self.events != std::ptr::null_mut() {
500 sc_app_layer_decoder_events_free_events(&mut self.events);
501 }
502 match self.de_state {
503 Some(state) => {
504 sc_detect_engine_state_free(state);
505 }
506 _ => {}
507 }
508 }
509}
510
511impl Drop for SMBTransaction {
512 fn drop(&mut self) {
513 self.free();
514 }
515}
516
517#[derive(Hash, Eq, PartialEq, Debug, Clone)]
518pub struct SMBFileGUIDOffset {
519 pub guid: Vec<u8>,
520 pub offset: u64,
521}
522
523impl SMBFileGUIDOffset {
524 pub fn new(guid: Vec<u8>, offset: u64) -> SMBFileGUIDOffset {
525 SMBFileGUIDOffset {
526 guid:guid,
527 offset:offset,
528 }
529 }
530}
531
532/// type values to make sure we're not mixing things
533/// up in hashmap lookups
534pub const SMBHDR_TYPE_GUID: u32 = 1;
535pub const SMBHDR_TYPE_SHARE: u32 = 2;
536pub const SMBHDR_TYPE_FILENAME: u32 = 3;
537pub const SMBHDR_TYPE_OFFSET: u32 = 4;
538pub const SMBHDR_TYPE_GENERICTX: u32 = 5;
539pub const SMBHDR_TYPE_HEADER: u32 = 6;
540pub const SMBHDR_TYPE_MAX_SIZE: u32 = 7; // max resp size for SMB1_COMMAND_TRANS
541pub const SMBHDR_TYPE_TXNAME: u32 = 8; // SMB1_COMMAND_TRANS tx_name
542pub const SMBHDR_TYPE_TRANS_FRAG: u32 = 9;
543pub const SMBHDR_TYPE_TREE: u32 = 10;
544pub const SMBHDR_TYPE_DCERPCTX: u32 = 11;
545
546#[derive(Hash, Eq, PartialEq, Debug)]
547pub struct SMBCommonHdr {
548 pub ssn_id: u64,
549 pub tree_id: u32,
550 pub rec_type: u32,
551 pub msg_id: u64,
552}
553
554impl SMBCommonHdr {
555 pub fn init() -> SMBCommonHdr {
556 SMBCommonHdr {
557 rec_type : 0,
558 ssn_id : 0,
559 tree_id : 0,
560 msg_id : 0,
561 }
562 }
563 pub fn new(rec_type: u32, ssn_id: u64, tree_id: u32, msg_id: u64) -> SMBCommonHdr {
564 SMBCommonHdr {
565 rec_type : rec_type,
566 ssn_id : ssn_id,
567 tree_id : tree_id,
568 msg_id : msg_id,
569 }
570 }
571 pub fn from2(r: &Smb2Record, rec_type: u32) -> SMBCommonHdr {
572 let tree_id = match rec_type {
573 SMBHDR_TYPE_TREE => { 0 },
574 _ => r.tree_id,
575 };
576 let msg_id = match rec_type {
be615c9f 577 SMBHDR_TYPE_TRANS_FRAG | SMBHDR_TYPE_SHARE => { 0 },
75d7c9d6
VJ
578 _ => { r.message_id as u64 },
579 };
580
581 SMBCommonHdr {
582 rec_type : rec_type,
583 ssn_id : r.session_id,
584 tree_id : tree_id,
585 msg_id : msg_id,
586 }
587
588 }
589 pub fn from1(r: &SmbRecord, rec_type: u32) -> SMBCommonHdr {
590 let tree_id = match rec_type {
591 SMBHDR_TYPE_TREE => { 0 },
592 _ => r.tree_id as u32,
593 };
594 let msg_id = match rec_type {
be615c9f 595 SMBHDR_TYPE_TRANS_FRAG | SMBHDR_TYPE_SHARE => { 0 },
75d7c9d6
VJ
596 _ => { r.multiplex_id as u64 },
597 };
598
599 SMBCommonHdr {
600 rec_type : rec_type,
601 ssn_id : r.ssn_id as u64,
602 tree_id : tree_id,
603 msg_id : msg_id,
604 }
605 }
606}
607
608#[derive(Hash, Eq, PartialEq, Debug)]
609pub struct SMBHashKeyHdrGuid {
610 hdr: SMBCommonHdr,
611 guid: Vec<u8>,
612}
613
614impl SMBHashKeyHdrGuid {
615 pub fn new(hdr: SMBCommonHdr, guid: Vec<u8>) -> SMBHashKeyHdrGuid {
616 SMBHashKeyHdrGuid {
617 hdr: hdr, guid: guid,
618 }
619 }
620}
621
622#[derive(Hash, Eq, PartialEq, Debug)]
623pub struct SMBTree {
624 pub name: Vec<u8>,
625 pub is_pipe: bool,
626}
627
628impl SMBTree {
629 pub fn new(name: Vec<u8>, is_pipe: bool) -> SMBTree {
630 SMBTree {
631 name:name,
632 is_pipe:is_pipe,
633 }
634 }
635}
636
637pub fn u32_as_bytes(i: u32) -> [u8;4] {
638 let o1: u8 = ((i >> 24) & 0xff) as u8;
639 let o2: u8 = ((i >> 16) & 0xff) as u8;
640 let o3: u8 = ((i >> 8) & 0xff) as u8;
641 let o4: u8 = (i & 0xff) as u8;
642 return [o1, o2, o3, o4]
643}
644
645pub struct SMBState<> {
646 /// map ssn/tree/msgid to vec (guid/name/share)
647 pub ssn2vec_map: HashMap<SMBCommonHdr, Vec<u8>>,
648 /// map guid to filename
649 pub guid2name_map: HashMap<Vec<u8>, Vec<u8>>,
650 /// map ssn key to read offset
651 pub ssn2vecoffset_map: HashMap<SMBCommonHdr, SMBFileGUIDOffset>,
652
653 pub ssn2tree_map: HashMap<SMBCommonHdr, SMBTree>,
654
655 // track the max size we expect for TRANS responses
656 pub ssn2maxsize_map: HashMap<SMBCommonHdr, u16>,
657 pub ssnguid2vec_map: HashMap<SMBHashKeyHdrGuid, Vec<u8>>,
658
659 /// TCP segments defragmentation buffer
660 pub tcp_buffer_ts: Vec<u8>,
661 pub tcp_buffer_tc: Vec<u8>,
662
663 pub files: SMBFiles,
664
665 pub skip_ts: u32,
666 pub skip_tc: u32,
667
668 pub file_ts_left : u32,
669 pub file_tc_left : u32,
670 pub file_ts_guid : Vec<u8>,
671 pub file_tc_guid : Vec<u8>,
672
673 pub ts_ssn_gap: bool,
674 pub tc_ssn_gap: bool,
675
676 pub ts_gap: bool, // last TS update was gap
677 pub tc_gap: bool, // last TC update was gap
678
8bef1208
VJ
679 pub ts_trunc: bool, // no more data for TOSERVER
680 pub tc_trunc: bool, // no more data for TOCLIENT
75d7c9d6
VJ
681
682 /// transactions list
683 pub transactions: Vec<SMBTransaction>,
684
685 /// tx counter for assigning incrementing id's to tx's
686 tx_id: u64,
687
0d69e7b8 688 /// SMB2 dialect or 0 if not set or SMB1
75d7c9d6 689 pub dialect: u16,
0d69e7b8 690 /// contains name of SMB1 dialect
75d7c9d6 691 pub dialect_vec: Option<Vec<u8>>, // used if dialect == 0
75d7c9d6
VJ
692
693 /// dcerpc interfaces, stored here to be able to match
694 /// them while inspecting DCERPC REQUEST txs
695 pub dcerpc_ifaces: Option<Vec<DCERPCIface>>,
75d7c9d6
VJ
696}
697
698impl SMBState {
699 /// Allocation function for a new TLS parser instance
700 pub fn new() -> SMBState {
701 SMBState {
702 ssn2vec_map:HashMap::new(),
703 guid2name_map:HashMap::new(),
704 ssn2vecoffset_map:HashMap::new(),
705 ssn2tree_map:HashMap::new(),
706 ssn2maxsize_map:HashMap::new(),
707 ssnguid2vec_map:HashMap::new(),
708 tcp_buffer_ts:Vec::new(),
709 tcp_buffer_tc:Vec::new(),
710 files: SMBFiles::new(),
711 skip_ts:0,
712 skip_tc:0,
713 file_ts_left:0,
714 file_tc_left:0,
715 file_ts_guid:Vec::new(),
716 file_tc_guid:Vec::new(),
717 ts_ssn_gap: false,
718 tc_ssn_gap: false,
719 ts_gap: false,
720 tc_gap: false,
721 ts_trunc: false,
722 tc_trunc: false,
723 transactions: Vec::new(),
724 tx_id:0,
725 dialect:0,
726 dialect_vec: None,
75d7c9d6 727 dcerpc_ifaces: None,
75d7c9d6
VJ
728 }
729 }
730
731 pub fn free(&mut self) {
732 //self._debug_state_stats();
733 self._debug_tx_stats();
734 self.files.free();
735 }
736
737 pub fn new_tx(&mut self) -> SMBTransaction {
738 let mut tx = SMBTransaction::new();
739 self.tx_id += 1;
740 tx.id = self.tx_id;
741 SCLogDebug!("TX {} created", tx.id);
742 return tx;
743 }
744
745 pub fn free_tx(&mut self, tx_id: u64) {
746 SCLogDebug!("Freeing TX with ID {} TX.ID {}", tx_id, tx_id+1);
747 let len = self.transactions.len();
748 let mut found = false;
749 let mut index = 0;
750 for i in 0..len {
751 let tx = &self.transactions[i];
752 if tx.id == tx_id + 1 {
753 found = true;
754 index = i;
755 SCLogDebug!("tx {} progress {}/{}", tx.id, tx.request_done, tx.response_done);
756 break;
757 }
758 }
759 if found {
760 SCLogDebug!("freeing TX with ID {} TX.ID {} at index {} left: {} max id: {}",
761 tx_id, tx_id+1, index, self.transactions.len(), self.tx_id);
762 self.transactions.remove(index);
763 }
764 }
765
766 // for use with the C API call StateGetTxIterator
767 pub fn get_tx_iterator(&mut self, min_tx_id: u64, state: &mut u64) ->
768 Option<(&SMBTransaction, u64, bool)>
769 {
770 let mut index = *state as usize;
771 let len = self.transactions.len();
772
773 // find tx that is >= min_tx_id
774 while index < len {
775 let tx = &self.transactions[index];
776 if tx.id < min_tx_id + 1 {
777 index += 1;
778 continue;
779 }
780 *state = index as u64 + 1;
781 //SCLogDebug!("returning tx_id {} has_next? {} (len {} index {}), tx {:?}",
782 // tx.id - 1, (len - index) > 1, len, index, tx);
783 return Some((tx, tx.id - 1, (len - index) > 1));
784 }
785 return None;
786 }
787
788 pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SMBTransaction> {
789/*
790 if self.transactions.len() > 100 {
791 SCLogNotice!("get_tx_by_id: tx_id={} in list={}", tx_id, self.transactions.len());
792 self._dump_txs();
793 panic!("txs exploded");
794 }
795*/
796 for tx in &mut self.transactions {
797 if tx.id == tx_id + 1 {
798 let ver = tx.vercmd.get_version();
799 let mut _smbcmd;
800 if ver == 2 {
801 let (_, cmd) = tx.vercmd.get_smb2_cmd();
802 _smbcmd = cmd;
803 } else {
804 let (_, cmd) = tx.vercmd.get_smb1_cmd();
805 _smbcmd = cmd as u16;
806 }
807 SCLogDebug!("Found SMB TX: id {} ver:{} cmd:{} progress {}/{} type_data {:?}",
808 tx.id, ver, _smbcmd, tx.request_done, tx.response_done, tx.type_data);
809 return Some(tx);
810 }
811 }
812 SCLogDebug!("Failed to find SMB TX with ID {}", tx_id);
813 return None;
814 }
815
816 /* generic TX has no type_data and is only used to
817 * track a single cmd request/reply pair. */
818
819 pub fn new_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: SMBCommonHdr)
820 -> (&mut SMBTransaction)
821 {
822 let mut tx = self.new_tx();
823 if smb_ver == 1 && smb_cmd <= 255 {
824 tx.vercmd.set_smb1_cmd(smb_cmd as u8);
825 } else if smb_ver == 2 {
826 tx.vercmd.set_smb2_cmd(smb_cmd);
827 }
828
829 tx.type_data = None;
830 tx.request_done = true;
831 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
832 tx.hdr = key;
833
834 SCLogDebug!("SMB: TX GENERIC created: ID {} tx list {} {:?}",
835 tx.id, self.transactions.len(), &tx);
836 self.transactions.push(tx);
837 let tx_ref = self.transactions.last_mut();
838 return tx_ref.unwrap();
839 }
840
841 pub fn get_last_tx(&mut self, smb_ver: u8, smb_cmd: u16)
842 -> Option<&mut SMBTransaction>
843 {
844 let tx_ref = self.transactions.last_mut();
845 match tx_ref {
846 Some(tx) => {
847 let found = if tx.vercmd.get_version() == smb_ver {
848 if smb_ver == 1 {
849 let (_, cmd) = tx.vercmd.get_smb1_cmd();
850 cmd as u16 == smb_cmd
851 } else if smb_ver == 2 {
852 let (_, cmd) = tx.vercmd.get_smb2_cmd();
853 cmd == smb_cmd
854 } else {
855 false
856 }
857 } else {
858 false
859 };
860 if found {
861 return Some(tx);
862 }
863 },
864 None => { },
865 }
866 return None;
867 }
868
869 pub fn get_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: &SMBCommonHdr)
870 -> Option<&mut SMBTransaction>
871 {
872 for tx in &mut self.transactions {
873 let found = if tx.vercmd.get_version() == smb_ver {
874 if smb_ver == 1 {
875 let (_, cmd) = tx.vercmd.get_smb1_cmd();
876 cmd as u16 == smb_cmd && tx.hdr == *key
877 } else if smb_ver == 2 {
878 let (_, cmd) = tx.vercmd.get_smb2_cmd();
879 cmd == smb_cmd && tx.hdr == *key
880 } else {
881 false
882 }
883 } else {
884 false
885 };
886 if found {
887 return Some(tx);
888 }
889 }
890 return None;
891 }
892
893 pub fn new_negotiate_tx(&mut self, smb_ver: u8)
894 -> (&mut SMBTransaction)
895 {
896 let mut tx = self.new_tx();
897 if smb_ver == 1 {
898 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_NEGOTIATE_PROTOCOL);
899 } else if smb_ver == 2 {
900 tx.vercmd.set_smb2_cmd(SMB2_COMMAND_NEGOTIATE_PROTOCOL);
901 }
902
903 tx.type_data = Some(SMBTransactionTypeData::NEGOTIATE(
904 SMBTransactionNegotiate::new(smb_ver)));
905 tx.request_done = true;
906 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
907
908 SCLogDebug!("SMB: TX NEGOTIATE created: ID {} SMB ver {}", tx.id, smb_ver);
909 self.transactions.push(tx);
910 let tx_ref = self.transactions.last_mut();
911 return tx_ref.unwrap();
912 }
913
914 pub fn get_negotiate_tx(&mut self, smb_ver: u8)
915 -> Option<&mut SMBTransaction>
916 {
917 for tx in &mut self.transactions {
918 let found = match tx.type_data {
919 Some(SMBTransactionTypeData::NEGOTIATE(ref x)) => {
920 if x.smb_ver == smb_ver {
921 true
922 } else {
923 false
924 }
925 },
926 _ => { false },
927 };
928 if found {
929 return Some(tx);
930 }
931 }
932 return None;
933 }
934
935 pub fn new_treeconnect_tx(&mut self, hdr: SMBCommonHdr, name: Vec<u8>)
936 -> (&mut SMBTransaction)
937 {
938 let mut tx = self.new_tx();
939
940 tx.hdr = hdr;
941 tx.type_data = Some(SMBTransactionTypeData::TREECONNECT(
942 SMBTransactionTreeConnect::new(name.to_vec())));
943 tx.request_done = true;
944 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
945
946 SCLogDebug!("SMB: TX TREECONNECT created: ID {} NAME {}",
947 tx.id, String::from_utf8_lossy(&name));
948 self.transactions.push(tx);
949 let tx_ref = self.transactions.last_mut();
950 return tx_ref.unwrap();
951 }
952
953 pub fn get_treeconnect_tx(&mut self, hdr: SMBCommonHdr)
954 -> Option<&mut SMBTransaction>
955 {
956 for tx in &mut self.transactions {
957 let hit = tx.hdr == hdr && match tx.type_data {
958 Some(SMBTransactionTypeData::TREECONNECT(_)) => { true },
959 _ => { false },
960 };
961 if hit {
962 return Some(tx);
963 }
964 }
965 return None;
966 }
967
968 pub fn new_create_tx(&mut self, file_name: &Vec<u8>,
969 disposition: u32, del: bool, dir: bool,
970 hdr: SMBCommonHdr)
971 -> &mut SMBTransaction
972 {
973 let mut tx = self.new_tx();
974 tx.hdr = hdr;
975 tx.type_data = Some(SMBTransactionTypeData::CREATE(
976 SMBTransactionCreate::new(
977 file_name.to_vec(), disposition,
978 del, dir)));
979 tx.request_done = true;
980 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
981
982 self.transactions.push(tx);
983 let tx_ref = self.transactions.last_mut();
984 return tx_ref.unwrap();
985 }
986
987 pub fn get_create_tx_by_hdr(&mut self, hdr: &SMBCommonHdr)
988 -> Option<&mut SMBTransaction>
989 {
990 for tx in &mut self.transactions {
991 let found = match tx.type_data {
992 Some(SMBTransactionTypeData::CREATE(ref _d)) => {
993 *hdr == tx.hdr
994 },
995 _ => { false },
996 };
997
998 if found {
999 SCLogDebug!("SMB: Found SMB create TX with ID {}", tx.id);
1000 return Some(tx);
1001 }
1002 }
1003 SCLogDebug!("SMB: Failed to find SMB create TX with key {:?}", hdr);
1004 return None;
1005 }
1006
1007 pub fn get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool)
1008 {
1009 let (name, is_dcerpc) = match self.guid2name_map.get(&guid.to_vec()) {
1010 Some(n) => {
1011 match str::from_utf8(&n) {
1012 Ok("PSEXESVC") => ("PSEXESVC", false),
1013 Ok("svcctl") => ("svcctl", true),
1014 Ok("srvsvc") => ("srvsvc", true),
1015 Ok("atsvc") => ("atsvc", true),
1016 Ok("lsarpc") => ("lsarpc", true),
1017 Ok("samr") => ("samr", true),
1018 Err(_) => ("MALFORMED", false),
1019 Ok(&_) => {
1020 SCLogNotice!("don't know {}", String::from_utf8_lossy(&n));
1021 ("UNKNOWN", false)
1022 },
1023 }
1024 },
1025 _ => { ("UNKNOWN", false) },
1026 };
1027 SCLogDebug!("service {} is_dcerpc {}", name, is_dcerpc);
1028 (&name, is_dcerpc)
1029 }
1030
1031 /* if we have marked the ssn as 'gapped' we check to see if
1032 * we've caught up. The check is to see if we have the last
a44504a1
VJ
1033 * tx in our list is done. This means we've seen both sides
1034 * and we're back in sync. Mark older txs as 'done' */
1035 fn check_gap_resync(&mut self, prior_max_id: u64)
75d7c9d6 1036 {
a44504a1
VJ
1037 SCLogDebug!("check_gap_resync2: post-GAP resync check ({}/{})", self.ts_ssn_gap, self.tc_ssn_gap);
1038 if !self.ts_ssn_gap && !self.tc_ssn_gap {
1039 return;
1040 }
1041
1042 let (last_done, id) = match self.transactions.last() {
1043 Some(tx) => {
1044 (tx.request_done && tx.response_done, tx.id)
1045 },
1046 None => (false, 0),
1047 };
1048 if last_done && id > 0 {
1049 SCLogNotice!("check_gap_resync2: TX {} is done post-GAP, mark all older ones complete", id);
1050 self.ts_ssn_gap = false;
1051 self.tc_ssn_gap = false;
1052 self.close_non_file_txs(prior_max_id);
75d7c9d6
VJ
1053 }
1054 }
1055
1056 /* close all txs execpt file xfers. */
a44504a1
VJ
1057 fn close_non_file_txs(&mut self, max_id: u64) {
1058 SCLogDebug!("close_non_file_txs: checking for non-file txs to wrap up");
75d7c9d6 1059 for tx in &mut self.transactions {
a44504a1
VJ
1060 if tx.id >= max_id {
1061 SCLogDebug!("close_non_file_txs: done");
1062 break;
1063 }
1064 if let Some(SMBTransactionTypeData::FILE(_)) = tx.type_data {
1065 // leaving FILE txs open as they can deal with gaps.
1066 } else {
1067 SCLogDebug!("ose_non_file_txs: tx {} marked as done", tx.id);
1068 tx.request_done = true;
1069 tx.response_done = true;
75d7c9d6
VJ
1070 }
1071 }
1072 }
1073
1074 // return how much data we consumed
1075 fn handle_skip(&mut self, direction: u8, input_size: u32) -> u32 {
1076 let mut skip_left = if direction == STREAM_TOSERVER {
1077 self.skip_ts
1078 } else {
1079 self.skip_tc
1080 };
1081 if skip_left == 0 {
1082 return 0
1083 }
1084 SCLogDebug!("skip_left {} input_size {}", skip_left, input_size);
1085
1086 let consumed = if skip_left >= input_size {
1087 input_size
1088 } else {
1089 skip_left
1090 };
1091
1092 if skip_left <= input_size {
1093 skip_left = 0;
1094 } else {
1095 skip_left -= input_size;
1096 }
1097
1098 if direction == STREAM_TOSERVER {
1099 self.skip_ts = skip_left;
1100 } else {
1101 self.skip_tc = skip_left;
1102 }
1103 return consumed;
1104 }
1105
1106 /// return bytes consumed
1107 pub fn parse_tcp_data_ts_partial<'b>(&mut self, input: &'b[u8]) -> usize
1108 {
1109 SCLogDebug!("incomplete of size {}", input.len());
1110 if input.len() < 512 {
7dff9b99
VJ
1111 // check for malformed data. Wireshark reports as
1112 // 'NBSS continuation data'. If it's invalid we're
1113 // lost so we give up.
1114 if input.len() > 8 {
1115 match parse_nbss_record_partial(input) {
1116 IResult::Done(_, ref hdr) => {
1117 if !hdr.is_smb() {
1118 SCLogDebug!("partial NBSS, not SMB and no known msg type {}", hdr.message_type);
1119 self.trunc_ts();
1120 return 0;
1121 }
1122 },
1123 _ => {},
1124 }
1125 }
75d7c9d6
VJ
1126 return 0;
1127 }
1128
1129 match parse_nbss_record_partial(input) {
1130 IResult::Done(output, ref nbss_part_hdr) => {
1131 SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len());
1132 if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1133 match parse_smb_version(&nbss_part_hdr.data) {
1134 IResult::Done(_, ref smb) => {
1135 SCLogDebug!("SMB {:?}", smb);
b3439205 1136 if smb.version == 0xff_u8 { // SMB1
75d7c9d6
VJ
1137 SCLogDebug!("SMBv1 record");
1138 match parse_smb_record(&nbss_part_hdr.data) {
1139 IResult::Done(_, ref r) => {
1140 if r.command == SMB1_COMMAND_WRITE_ANDX {
1141 // see if it's a write to a pipe. We only handle those
1142 // if complete.
1143 let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
1144 r.ssn_id as u64, r.tree_id as u32, 0);
1145 let is_pipe = match self.ssn2tree_map.get(&tree_key) {
1146 Some(n) => n.is_pipe,
1147 None => false,
1148 };
1149 if is_pipe {
1150 return 0;
1151 }
1152 smb1_write_request_record(self, r);
1153 let consumed = input.len() - output.len();
1154 return consumed;
1155 }
1156 },
1157 _ => { },
1158
1159 }
b3439205 1160 } else if smb.version == 0xfe_u8 { // SMB2
75d7c9d6
VJ
1161 SCLogDebug!("SMBv2 record");
1162 match parse_smb2_request_record(&nbss_part_hdr.data) {
1163 IResult::Done(_, ref smb_record) => {
1164 SCLogDebug!("SMB2: partial record {}",
1165 &smb2_command_string(smb_record.command));
1166 if smb_record.command == SMB2_COMMAND_WRITE {
1167 smb2_write_request_record(self, smb_record);
1168 let consumed = input.len() - output.len();
1169 return consumed;
1170 }
1171 },
1172 _ => { },
1173 }
1174 }
b3439205 1175 // no SMB3 here yet, will buffer full records
75d7c9d6
VJ
1176 },
1177 _ => { },
1178 }
1179 }
1180 },
1181 _ => { },
1182 }
1183
1184 return 0;
1185 }
1186
1187 /// Parsing function, handling TCP chunks fragmentation
1188 pub fn parse_tcp_data_ts<'b>(&mut self, i: &'b[u8]) -> u32
1189 {
a44504a1 1190 let max_tx_id = self.tx_id;
75d7c9d6
VJ
1191
1192 let mut v : Vec<u8>;
1193 //println!("parse_tcp_data_ts ({})",i.len());
1194 //println!("{:?}",i);
1195 // Check if TCP data is being defragmented
1196 let tcp_buffer = match self.tcp_buffer_ts.len() {
1197 0 => i,
1198 _ => {
1199 v = self.tcp_buffer_ts.split_off(0);
1200 if self.tcp_buffer_ts.len() + i.len() > 100000 {
1201 self.set_event(SMBEvent::RecordOverflow);
1202 return 1;
1203 };
1204 v.extend_from_slice(i);
1205 v.as_slice()
1206 },
1207 };
1208 //println!("tcp_buffer ({})",tcp_buffer.len());
1209 let mut cur_i = tcp_buffer;
1210 if cur_i.len() > 1000000 {
1211 self.set_event(SMBEvent::RecordOverflow);
1212 return 1;
1213 }
1214 let consumed = self.handle_skip(STREAM_TOSERVER, cur_i.len() as u32);
1215 if consumed > 0 {
1216 if consumed > cur_i.len() as u32 {
1217 self.set_event(SMBEvent::InternalError);
1218 return 1;
1219 }
1220 cur_i = &cur_i[consumed as usize..];
1221 }
1222 // take care of in progress file chunk transfers
1223 // and skip buffer beyond it
1224 let consumed = self.filetracker_update(STREAM_TOSERVER, cur_i, 0);
1225 if consumed > 0 {
1226 if consumed > cur_i.len() as u32 {
1227 self.set_event(SMBEvent::InternalError);
1228 return 1;
1229 }
1230 cur_i = &cur_i[consumed as usize..];
1231 }
1232 // gap
1233 if self.ts_gap {
a44504a1 1234 SCLogDebug!("TS trying to catch up after GAP (input {})", cur_i.len());
b3439205 1235 match search_smb_record(cur_i) {
75d7c9d6
VJ
1236 IResult::Done(_, pg) => {
1237 SCLogDebug!("smb record found");
1238 let smb2_offset = cur_i.len() - pg.data.len();
1239 if smb2_offset < 4 {
1240 return 0;
1241 }
1242 let nbss_offset = smb2_offset - 4;
1243 cur_i = &cur_i[nbss_offset..];
1244
1245 self.ts_gap = false;
1246 },
1247 _ => {
1248 SCLogDebug!("smb record NOT found");
1249 self.tcp_buffer_ts.extend_from_slice(cur_i);
1250 return 0;
1251 },
1252 }
1253 }
1254 while cur_i.len() > 0 { // min record size
1255 match parse_nbss_record(cur_i) {
1256 IResult::Done(rem, ref nbss_hdr) => {
1257 if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1258 // we have the full records size worth of data,
1259 // let's parse it
1260 match parse_smb_version(&nbss_hdr.data) {
1261 IResult::Done(_, ref smb) => {
1262 SCLogDebug!("SMB {:?}", smb);
b3439205 1263 if smb.version == 0xff_u8 { // SMB1
75d7c9d6
VJ
1264 SCLogDebug!("SMBv1 record");
1265 match parse_smb_record(&nbss_hdr.data) {
1266 IResult::Done(_, ref smb_record) => {
1267 smb1_request_record(self, smb_record);
1268 },
1269 _ => {
1270 self.set_event(SMBEvent::MalformedData);
1271 return 1;
1272 },
1273 }
b3439205 1274 } else if smb.version == 0xfe_u8 { // SMB2
75d7c9d6
VJ
1275 let mut nbss_data = nbss_hdr.data;
1276 while nbss_data.len() > 0 {
1277 SCLogDebug!("SMBv2 record");
1278 match parse_smb2_request_record(&nbss_data) {
1279 IResult::Done(nbss_data_rem, ref smb_record) => {
1280 SCLogDebug!("nbss_data_rem {}", nbss_data_rem.len());
1281
1282 smb2_request_record(self, smb_record);
1283 nbss_data = nbss_data_rem;
1284 },
1285 _ => {
1286 self.set_event(SMBEvent::MalformedData);
1287 return 1;
1288 },
1289 }
1290 }
b3439205
VJ
1291 } else if smb.version == 0xfd_u8 { // SMB3 transform
1292 let mut nbss_data = nbss_hdr.data;
1293 while nbss_data.len() > 0 {
1294 SCLogDebug!("SMBv3 transform record");
1295 match parse_smb3_transform_record(&nbss_data) {
1296 IResult::Done(nbss_data_rem, ref _smb3_record) => {
1297 nbss_data = nbss_data_rem;
1298 },
1299 _ => {
1300 self.set_event(SMBEvent::MalformedData);
1301 return 1;
1302 },
1303 }
1304 }
75d7c9d6
VJ
1305 }
1306 },
1307 _ => {
1308 self.set_event(SMBEvent::MalformedData);
1309 return 1;
1310 },
1311 }
1312 } else {
1313 SCLogDebug!("NBSS message {:X}", nbss_hdr.message_type);
1314 }
1315 cur_i = rem;
1316 },
1317 IResult::Incomplete(_) => {
1318 let consumed = self.parse_tcp_data_ts_partial(cur_i);
1319 cur_i = &cur_i[consumed ..];
1320
1321 self.tcp_buffer_ts.extend_from_slice(cur_i);
1322 break;
1323 },
1324 IResult::Error(_) => {
1325 self.set_event(SMBEvent::MalformedData);
1326 return 1;
1327 },
1328 }
1329 };
1330
a44504a1 1331 self.check_gap_resync(max_tx_id);
75d7c9d6
VJ
1332 0
1333 }
1334
1335 /// return bytes consumed
1336 pub fn parse_tcp_data_tc_partial<'b>(&mut self, input: &'b[u8]) -> usize
1337 {
1338 SCLogDebug!("incomplete of size {}", input.len());
1339 if input.len() < 512 {
7dff9b99
VJ
1340 // check for malformed data. Wireshark reports as
1341 // 'NBSS continuation data'. If it's invalid we're
1342 // lost so we give up.
1343 if input.len() > 8 {
1344 match parse_nbss_record_partial(input) {
1345 IResult::Done(_, ref hdr) => {
1346 if !hdr.is_smb() {
1347 SCLogDebug!("partial NBSS, not SMB and no known msg type {}", hdr.message_type);
1348 self.trunc_tc();
1349 return 0;
1350 }
1351 },
1352 _ => {},
1353 }
1354 }
75d7c9d6
VJ
1355 return 0;
1356 }
1357
1358 match parse_nbss_record_partial(input) {
1359 IResult::Done(output, ref nbss_part_hdr) => {
1360 SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len());
1361 if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1362 match parse_smb_version(&nbss_part_hdr.data) {
1363 IResult::Done(_, ref smb) => {
1364 SCLogDebug!("SMB {:?}", smb);
1365 if smb.version == 255u8 { // SMB1
1366 SCLogDebug!("SMBv1 record");
1367 match parse_smb_record(&nbss_part_hdr.data) {
1368 IResult::Done(_, ref r) => {
1369 SCLogDebug!("SMB1: partial record {}",
1370 r.command);
1371 if r.command == SMB1_COMMAND_READ_ANDX {
1372 let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
1373 r.ssn_id as u64, r.tree_id as u32, 0);
1374 let is_pipe = match self.ssn2tree_map.get(&tree_key) {
1375 Some(n) => n.is_pipe,
1376 None => false,
1377 };
1378 if is_pipe {
1379 return 0;
1380 }
1381 smb1_read_response_record(self, r);
1382 let consumed = input.len() - output.len();
1383 return consumed;
1384 }
1385 },
1386 _ => { },
1387 }
1388 } else if smb.version == 254u8 { // SMB2
1389 SCLogDebug!("SMBv2 record");
1390 match parse_smb2_response_record(&nbss_part_hdr.data) {
1391 IResult::Done(_, ref smb_record) => {
1392 SCLogDebug!("SMB2: partial record {}",
1393 &smb2_command_string(smb_record.command));
1394 if smb_record.command == SMB2_COMMAND_READ {
1395 smb2_read_response_record(self, smb_record);
1396 let consumed = input.len() - output.len();
1397 return consumed;
1398 }
1399 },
1400 _ => { },
1401 }
1402 }
b3439205 1403 // no SMB3 here yet, will buffer full records
75d7c9d6
VJ
1404 },
1405 _ => { },
1406 }
1407 }
1408 },
1409 _ => { },
1410 }
1411
1412 return 0;
1413 }
1414
1415 /// Parsing function, handling TCP chunks fragmentation
1416 pub fn parse_tcp_data_tc<'b>(&mut self, i: &'b[u8]) -> u32
1417 {
a44504a1 1418 let max_tx_id = self.tx_id;
75d7c9d6
VJ
1419
1420 let mut v : Vec<u8>;
1421 // Check if TCP data is being defragmented
1422 let tcp_buffer = match self.tcp_buffer_tc.len() {
1423 0 => i,
1424 _ => {
1425 v = self.tcp_buffer_tc.split_off(0);
1426 if self.tcp_buffer_tc.len() + i.len() > 100000 {
1427 self.set_event(SMBEvent::RecordOverflow);
1428 return 1;
1429 };
1430 v.extend_from_slice(i);
1431 v.as_slice()
1432 },
1433 };
1434 let mut cur_i = tcp_buffer;
1435 SCLogDebug!("cur_i.len {}", cur_i.len());
1436 if cur_i.len() > 100000 {
1437 self.set_event(SMBEvent::RecordOverflow);
1438 return 1;
1439 }
1440 let consumed = self.handle_skip(STREAM_TOCLIENT, cur_i.len() as u32);
1441 if consumed > 0 {
1442 if consumed > cur_i.len() as u32 {
1443 self.set_event(SMBEvent::InternalError);
1444 return 1;
1445 }
1446 cur_i = &cur_i[consumed as usize..];
1447 }
1448 // take care of in progress file chunk transfers
1449 // and skip buffer beyond it
1450 let consumed = self.filetracker_update(STREAM_TOCLIENT, cur_i, 0);
1451 if consumed > 0 {
1452 if consumed > cur_i.len() as u32 {
1453 self.set_event(SMBEvent::InternalError);
1454 return 1;
1455 }
1456 cur_i = &cur_i[consumed as usize..];
1457 }
1458 // gap
1459 if self.tc_gap {
b3439205
VJ
1460 SCLogDebug!("TC trying to catch up after GAP (input {})", cur_i.len());
1461 match search_smb_record(cur_i) {
75d7c9d6
VJ
1462 IResult::Done(_, pg) => {
1463 SCLogDebug!("smb record found");
1464 let smb2_offset = cur_i.len() - pg.data.len();
1465 if smb2_offset < 4 {
1466 return 0;
1467 }
1468 let nbss_offset = smb2_offset - 4;
1469 cur_i = &cur_i[nbss_offset..];
1470
1471 self.tc_gap = false;
1472 },
1473 _ => {
1474 SCLogDebug!("smb record NOT found");
1475 self.tcp_buffer_tc.extend_from_slice(cur_i);
1476 return 0;
1477 },
1478 }
1479 }
1480 while cur_i.len() > 0 { // min record size
1481 match parse_nbss_record(cur_i) {
1482 IResult::Done(rem, ref nbss_hdr) => {
1483 if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1484 // we have the full records size worth of data,
1485 // let's parse it
1486 match parse_smb_version(&nbss_hdr.data) {
1487 IResult::Done(_, ref smb) => {
1488 SCLogDebug!("SMB {:?}", smb);
b3439205 1489 if smb.version == 0xff_u8 { // SMB1
75d7c9d6
VJ
1490 SCLogDebug!("SMBv1 record");
1491 match parse_smb_record(&nbss_hdr.data) {
1492 IResult::Done(_, ref smb_record) => {
1493 smb1_response_record(self, smb_record);
1494 },
1495 _ => {
1496 self.set_event(SMBEvent::MalformedData);
1497 return 1;
1498 },
1499 }
b3439205 1500 } else if smb.version == 0xfe_u8 { // SMB2
75d7c9d6
VJ
1501 let mut nbss_data = nbss_hdr.data;
1502 while nbss_data.len() > 0 {
1503 SCLogDebug!("SMBv2 record");
1504 match parse_smb2_response_record(&nbss_data) {
1505 IResult::Done(nbss_data_rem, ref smb_record) => {
1506 smb2_response_record(self, smb_record);
1507 nbss_data = nbss_data_rem;
b3439205
VJ
1508 },
1509 _ => {
1510 self.set_event(SMBEvent::MalformedData);
1511 return 1;
1512 },
1513 }
1514 }
1515 } else if smb.version == 0xfd_u8 { // SMB3 transform
1516 let mut nbss_data = nbss_hdr.data;
1517 while nbss_data.len() > 0 {
1518 SCLogDebug!("SMBv3 transform record");
1519 match parse_smb3_transform_record(&nbss_data) {
1520 IResult::Done(nbss_data_rem, ref _smb3_record) => {
1521 nbss_data = nbss_data_rem;
75d7c9d6
VJ
1522 },
1523 _ => {
1524 self.set_event(SMBEvent::MalformedData);
1525 return 1;
1526 },
1527 }
1528 }
1529 }
1530 },
1531 IResult::Incomplete(_) => {
1532 // not enough data to contain basic SMB hdr
1533 // TODO event: empty NBSS_MSGTYPE_SESSION_MESSAGE
1534 },
1535 IResult::Error(_) => {
1536 self.set_event(SMBEvent::MalformedData);
1537 return 1;
1538 },
1539 }
1540 } else {
1541 SCLogDebug!("NBSS message {:X}", nbss_hdr.message_type);
1542 }
1543 cur_i = rem;
1544 },
7dff9b99
VJ
1545 IResult::Incomplete(needed) => {
1546 SCLogDebug!("INCOMPLETE have {} needed {:?}", cur_i.len(), needed);
75d7c9d6
VJ
1547 let consumed = self.parse_tcp_data_tc_partial(cur_i);
1548 cur_i = &cur_i[consumed ..];
1549
1550 SCLogDebug!("INCOMPLETE have {}", cur_i.len());
1551 self.tcp_buffer_tc.extend_from_slice(cur_i);
1552 break;
1553 },
1554 IResult::Error(_) => {
1555 self.set_event(SMBEvent::MalformedData);
1556 return 1;
1557 },
1558 }
1559 };
a44504a1 1560 self.check_gap_resync(max_tx_id);
28f16e38 1561 self._debug_tx_stats();
75d7c9d6
VJ
1562 0
1563 }
1564
1565 /// handle a gap in the TOSERVER direction
1566 /// returns: 0 ok, 1 unrecoverable error
1567 pub fn parse_tcp_data_ts_gap(&mut self, gap_size: u32) -> u32 {
1568 if self.tcp_buffer_ts.len() > 0 {
1569 self.tcp_buffer_ts.clear();
1570 }
1571 let consumed = self.handle_skip(STREAM_TOSERVER, gap_size);
1572 if consumed < gap_size {
1573 let new_gap_size = gap_size - consumed;
1574 let gap = vec![0; new_gap_size as usize];
1575
1576 let consumed2 = self.filetracker_update(STREAM_TOSERVER, &gap, new_gap_size);
1577 if consumed2 > new_gap_size {
1578 SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size);
1579 self.set_event(SMBEvent::InternalError);
1580 return 1;
1581 }
1582 }
1583 SCLogDebug!("GAP of size {} in toserver direction", gap_size);
1584 self.ts_ssn_gap = true;
1585 self.ts_gap = true;
1586 return 0
1587 }
1588
1589 /// handle a gap in the TOCLIENT direction
1590 /// returns: 0 ok, 1 unrecoverable error
1591 pub fn parse_tcp_data_tc_gap(&mut self, gap_size: u32) -> u32 {
1592 if self.tcp_buffer_tc.len() > 0 {
1593 self.tcp_buffer_tc.clear();
1594 }
1595 let consumed = self.handle_skip(STREAM_TOCLIENT, gap_size);
1596 if consumed < gap_size {
1597 let new_gap_size = gap_size - consumed;
1598 let gap = vec![0; new_gap_size as usize];
1599
1600 let consumed2 = self.filetracker_update(STREAM_TOCLIENT, &gap, new_gap_size);
1601 if consumed2 > new_gap_size {
1602 SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size);
1603 self.set_event(SMBEvent::InternalError);
1604 return 1;
1605 }
1606 }
1607 SCLogDebug!("GAP of size {} in toclient direction", gap_size);
1608 self.tc_ssn_gap = true;
1609 self.tc_gap = true;
1610 return 0
1611 }
1612
1613 pub fn trunc_ts(&mut self) {
1614 SCLogDebug!("TRUNC TS");
1615 self.ts_trunc = true;
7dff9b99 1616 self.tcp_buffer_ts.clear();
75d7c9d6
VJ
1617
1618 for tx in &mut self.transactions {
1619 if !tx.request_done {
1620 SCLogDebug!("TRUNCING TX {} in TOSERVER direction", tx.id);
1621 tx.request_done = true;
1622 }
1623 }
1624 }
1625 pub fn trunc_tc(&mut self) {
1626 SCLogDebug!("TRUNC TC");
1627 self.tc_trunc = true;
7dff9b99 1628 self.tcp_buffer_tc.clear();
75d7c9d6
VJ
1629
1630 for tx in &mut self.transactions {
1631 if !tx.response_done {
1632 SCLogDebug!("TRUNCING TX {} in TOCLIENT direction", tx.id);
1633 tx.response_done = true;
1634 }
1635 }
1636 }
1637}
1638
1639/// Returns *mut SMBState
1640#[no_mangle]
1641pub extern "C" fn rs_smb_state_new() -> *mut libc::c_void {
1642 let state = SMBState::new();
1643 let boxed = Box::new(state);
1644 SCLogDebug!("allocating state");
1645 return unsafe{transmute(boxed)};
1646}
1647
1648/// Params:
1649/// - state: *mut SMBState as void pointer
1650#[no_mangle]
1651pub extern "C" fn rs_smb_state_free(state: *mut libc::c_void) {
1652 // Just unbox...
1653 SCLogDebug!("freeing state");
1654 let mut smb_state: Box<SMBState> = unsafe{transmute(state)};
1655 smb_state.free();
1656}
1657
1658/// C binding parse a SMB request. Returns 1 on success, -1 on failure.
1659#[no_mangle]
1660pub extern "C" fn rs_smb_parse_request_tcp(_flow: *mut Flow,
1661 state: &mut SMBState,
1662 _pstate: *mut libc::c_void,
1663 input: *mut libc::uint8_t,
1664 input_len: libc::uint32_t,
1665 _data: *mut libc::c_void)
1666 -> libc::int8_t
1667{
1668 let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
1669 SCLogDebug!("parsing {} bytes of request data", input_len);
1670
1671 if state.parse_tcp_data_ts(buf) == 0 {
1672 return 1;
1673 } else {
1674 return -1;
1675 }
1676}
1677
1678#[no_mangle]
1679pub extern "C" fn rs_smb_parse_request_tcp_gap(
1680 state: &mut SMBState,
1681 input_len: libc::uint32_t)
1682 -> libc::int8_t
1683{
1684 if state.parse_tcp_data_ts_gap(input_len as u32) == 0 {
1685 return 1;
1686 }
1687 return -1;
1688}
1689
1690
1691#[no_mangle]
1692pub extern "C" fn rs_smb_parse_response_tcp(_flow: *mut Flow,
1693 state: &mut SMBState,
1694 _pstate: *mut libc::c_void,
1695 input: *mut libc::uint8_t,
1696 input_len: libc::uint32_t,
1697 _data: *mut libc::c_void)
1698 -> libc::int8_t
1699{
1700 SCLogDebug!("parsing {} bytes of response data", input_len);
1701 let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
1702
1703 if state.parse_tcp_data_tc(buf) == 0 {
1704 return 1;
1705 } else {
1706 return -1;
1707 }
1708}
1709
1710#[no_mangle]
1711pub extern "C" fn rs_smb_parse_response_tcp_gap(
1712 state: &mut SMBState,
1713 input_len: libc::uint32_t)
1714 -> libc::int8_t
1715{
1716 if state.parse_tcp_data_tc_gap(input_len as u32) == 0 {
1717 return 1;
1718 }
1719 return -1;
1720}
1721
75d7c9d6 1722#[no_mangle]
7ab071a5 1723pub extern "C" fn rs_smb_probe_tcp(input: *const libc::uint8_t, len: libc::uint32_t)
75d7c9d6
VJ
1724 -> libc::int8_t
1725{
7ab071a5
VJ
1726 let slice: &[u8] = unsafe {
1727 std::slice::from_raw_parts(input as *mut u8, len as usize)
1728 };
1729 match parse_nbss_record_partial(slice) {
1730 IResult::Done(_, ref hdr) => {
1731 if hdr.is_smb() {
1732 return 1;
1733 }
1734 },
1735 _ => { },
1736 }
75d7c9d6
VJ
1737 return 1
1738}
1739
1740#[no_mangle]
1741pub extern "C" fn rs_smb_state_get_tx_count(state: &mut SMBState)
1742 -> libc::uint64_t
1743{
1744 SCLogDebug!("rs_smb_state_get_tx_count: returning {}", state.tx_id);
1745 return state.tx_id;
1746}
1747
1748#[no_mangle]
1749pub extern "C" fn rs_smb_state_get_tx(state: &mut SMBState,
1750 tx_id: libc::uint64_t)
1751 -> *mut SMBTransaction
1752{
1753 match state.get_tx_by_id(tx_id) {
1754 Some(tx) => {
1755 return unsafe{transmute(tx)};
1756 }
1757 None => {
1758 return std::ptr::null_mut();
1759 }
1760 }
1761}
1762
1763// for use with the C API call StateGetTxIterator
1764#[no_mangle]
1765pub extern "C" fn rs_smb_state_get_tx_iterator(
1766 state: &mut SMBState,
1767 min_tx_id: libc::uint64_t,
1768 istate: &mut libc::uint64_t)
1769 -> applayer::AppLayerGetTxIterTuple
1770{
1771 match state.get_tx_iterator(min_tx_id, istate) {
1772 Some((tx, out_tx_id, has_next)) => {
1773 let c_tx = unsafe { transmute(tx) };
1774 let ires = applayer::AppLayerGetTxIterTuple::with_values(c_tx, out_tx_id, has_next);
1775 return ires;
1776 }
1777 None => {
1778 return applayer::AppLayerGetTxIterTuple::not_found();
1779 }
1780 }
1781}
1782
1783#[no_mangle]
1784pub extern "C" fn rs_smb_state_tx_free(state: &mut SMBState,
1785 tx_id: libc::uint64_t)
1786{
1787 SCLogDebug!("freeing tx {}", tx_id as u64);
1788 state.free_tx(tx_id);
1789}
1790
1791#[no_mangle]
1792pub extern "C" fn rs_smb_state_progress_completion_status(
1793 _direction: libc::uint8_t)
1794 -> libc::c_int
1795{
1796 return 1;
1797}
1798
1799#[no_mangle]
1800pub extern "C" fn rs_smb_tx_get_alstate_progress(tx: &mut SMBTransaction,
1801 direction: libc::uint8_t)
1802 -> libc::uint8_t
1803{
1804 if direction == STREAM_TOSERVER && tx.request_done {
1805 SCLogDebug!("tx {} TOSERVER progress 1 => {:?}", tx.id, tx);
1806 return 1;
1807 } else if direction == STREAM_TOCLIENT && tx.response_done {
1808 SCLogDebug!("tx {} TOCLIENT progress 1 => {:?}", tx.id, tx);
1809 return 1;
1810 } else {
1811 SCLogDebug!("tx {} direction {} progress 0", tx.id, direction);
1812 return 0;
1813 }
1814}
1815
1816#[no_mangle]
1817pub extern "C" fn rs_smb_tx_set_logged(_state: &mut SMBState,
1818 tx: &mut SMBTransaction,
1819 bits: libc::uint32_t)
1820{
1821 tx.logged.set(bits);
1822}
1823
1824#[no_mangle]
1825pub extern "C" fn rs_smb_tx_get_logged(_state: &mut SMBState,
1826 tx: &mut SMBTransaction)
1827 -> u32
1828{
1829 return tx.logged.get();
1830}
1831
1832#[no_mangle]
1833pub extern "C" fn rs_smb_tx_set_detect_flags(
1834 tx: &mut SMBTransaction,
1835 direction: libc::uint8_t,
1836 flags: libc::uint64_t)
1837{
1838 if (direction & STREAM_TOSERVER) != 0 {
1839 tx.detect_flags_ts = flags as u64;
1840 } else {
1841 tx.detect_flags_tc = flags as u64;
1842 }
1843}
1844
1845#[no_mangle]
1846pub extern "C" fn rs_smb_tx_get_detect_flags(
1847 tx: &mut SMBTransaction,
1848 direction: libc::uint8_t)
1849 -> libc::uint64_t
1850{
1851 if (direction & STREAM_TOSERVER) != 0 {
1852 return tx.detect_flags_ts as libc::uint64_t;
1853 } else {
1854 return tx.detect_flags_tc as libc::uint64_t;
1855 }
1856}
1857
1858#[no_mangle]
1859pub extern "C" fn rs_smb_state_set_tx_detect_state(
1860 tx: &mut SMBTransaction,
1861 de_state: &mut DetectEngineState)
1862{
1863 tx.de_state = Some(de_state);
1864}
1865
1866#[no_mangle]
1867pub extern "C" fn rs_smb_state_get_tx_detect_state(
1868 tx: &mut SMBTransaction)
1869 -> *mut DetectEngineState
1870{
1871 match tx.de_state {
1872 Some(ds) => {
1873 return ds;
1874 },
1875 None => {
1876 return std::ptr::null_mut();
1877 }
1878 }
1879}
1880
1881#[no_mangle]
1882pub extern "C" fn rs_smb_state_truncate(
1883 state: &mut SMBState,
1884 direction: libc::uint8_t)
1885{
1886 if (direction & STREAM_TOSERVER) != 0 {
1887 state.trunc_ts();
1888 } else {
1889 state.trunc_tc();
1890 }
1891}
1892
1893#[no_mangle]
1894pub extern "C" fn rs_smb_state_get_events(state: &mut SMBState,
1895 tx_id: libc::uint64_t)
1896 -> *mut AppLayerDecoderEvents
1897{
1898 match state.get_tx_by_id(tx_id) {
1899 Some(tx) => {
1900 return tx.events;
1901 }
1902 _ => {
1903 return std::ptr::null_mut();
1904 }
1905 }
1906}
1907
1908#[no_mangle]
1909pub extern "C" fn rs_smb_state_get_event_info(event_name: *const libc::c_char,
1910 event_id: *mut libc::c_int,
1911 event_type: *mut AppLayerEventType)
1912 -> i8
1913{
1914 if event_name == std::ptr::null() {
1915 return -1;
1916 }
1917 let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) };
1918 let event = match c_event_name.to_str() {
1919 Ok(s) => {
1920 smb_str_to_event(s)
1921 },
1922 Err(_) => -1, // UTF-8 conversion failed
1923 };
1924 unsafe {
1925 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
1926 *event_id = event as libc::c_int;
1927 };
1928 if event == -1 {
1929 return -1;
1930 }
1931 0
1932}