]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/smb/smb.rs
smb2: improve read/write record parsing
[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),
7b61f2c5
VJ
366 RENAME(SMBTransactionRename),
367}
368
369#[derive(Debug)]
370pub struct SMBTransactionRename {
371 pub oldname: Vec<u8>,
372 pub newname: Vec<u8>,
373 pub fuid: Vec<u8>,
374}
375
376impl SMBTransactionRename {
377 pub fn new(fuid: Vec<u8>, oldname: Vec<u8>, newname: Vec<u8>) -> SMBTransactionRename {
378 return SMBTransactionRename {
379 fuid: fuid, oldname: oldname, newname: newname,
380 }
381 }
382}
383
384impl SMBState {
385 pub fn new_rename_tx(&mut self, fuid: Vec<u8>, oldname: Vec<u8>, newname: Vec<u8>)
386 -> (&mut SMBTransaction)
387 {
388 let mut tx = self.new_tx();
389
390 tx.type_data = Some(SMBTransactionTypeData::RENAME(
391 SMBTransactionRename::new(fuid, oldname, newname)));
392 tx.request_done = true;
393 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
394
ea1e13cb 395 SCLogDebug!("SMB: TX RENAME created: ID {}", tx.id);
7b61f2c5
VJ
396 self.transactions.push(tx);
397 let tx_ref = self.transactions.last_mut();
398 return tx_ref.unwrap();
399 }
75d7c9d6
VJ
400}
401
402#[derive(Debug)]
403pub struct SMBTransactionCreate {
404 pub disposition: u32,
405 pub delete_on_close: bool,
406 pub directory: bool,
407 pub filename: Vec<u8>,
fb986abe 408 pub guid: Vec<u8>,
0e05ef73
VJ
409
410 pub create_ts: u32,
411 pub last_access_ts: u32,
412 pub last_write_ts: u32,
413 pub last_change_ts: u32,
414
415 pub size: u64,
75d7c9d6
VJ
416}
417
418impl SMBTransactionCreate {
419 pub fn new(filename: Vec<u8>, disp: u32, del: bool, dir: bool) -> SMBTransactionCreate {
420 return SMBTransactionCreate {
421 disposition: disp,
422 delete_on_close: del,
423 directory: dir,
424 filename: filename,
fb986abe 425 guid: Vec::new(),
0e05ef73
VJ
426 create_ts: 0,
427 last_access_ts: 0,
428 last_write_ts: 0,
429 last_change_ts: 0,
430 size: 0,
75d7c9d6
VJ
431 }
432 }
433}
434
435#[derive(Debug)]
436pub struct SMBTransactionNegotiate {
437 pub smb_ver: u8,
438 pub dialects: Vec<Vec<u8>>,
439 pub dialects2: Vec<Vec<u8>>,
6d56edc3
VJ
440
441 // SMB1 doesn't have the client GUID
442 pub client_guid: Option<Vec<u8>>,
443 pub server_guid: Vec<u8>,
75d7c9d6
VJ
444}
445
446impl SMBTransactionNegotiate {
447 pub fn new(smb_ver: u8) -> SMBTransactionNegotiate {
448 return SMBTransactionNegotiate {
449 smb_ver: smb_ver,
450 dialects: Vec::new(),
451 dialects2: Vec::new(),
6d56edc3
VJ
452 client_guid: None,
453 server_guid: Vec::with_capacity(16),
75d7c9d6
VJ
454 }
455 }
456}
457
458#[derive(Debug)]
459pub struct SMBTransactionTreeConnect {
460 pub is_pipe: bool,
c56f5e11 461 pub share_type: u8,
75d7c9d6
VJ
462 pub tree_id: u32,
463 pub share_name: Vec<u8>,
fcbeab70
VJ
464
465 /// SMB1 service strings
466 pub req_service: Option<Vec<u8>>,
467 pub res_service: Option<Vec<u8>>,
75d7c9d6
VJ
468}
469
470impl SMBTransactionTreeConnect {
471 pub fn new(share_name: Vec<u8>) -> SMBTransactionTreeConnect {
472 return SMBTransactionTreeConnect {
473 is_pipe:false,
c56f5e11 474 share_type: 0,
75d7c9d6
VJ
475 tree_id:0,
476 share_name:share_name,
fcbeab70
VJ
477 req_service: None,
478 res_service: None,
75d7c9d6
VJ
479 }
480 }
481}
482
483#[derive(Debug)]
484pub struct SMBTransaction {
485 pub id: u64, /// internal id
486
487 /// version, command and status
488 pub vercmd: SMBVerCmdStat,
489 /// session id, tree id, etc.
490 pub hdr: SMBCommonHdr,
491
492 /// for state tracking. false means this side is in progress, true
493 /// that it's complete.
494 pub request_done: bool,
495 pub response_done: bool,
496
497 /// Command specific data
498 pub type_data: Option<SMBTransactionTypeData>,
499
500 /// detection engine flags for use by detection engine
501 detect_flags_ts: u64,
502 detect_flags_tc: u64,
503 pub logged: LoggerFlags,
504 pub de_state: Option<*mut DetectEngineState>,
505 pub events: *mut AppLayerDecoderEvents,
506}
507
508impl SMBTransaction {
509 pub fn new() -> SMBTransaction {
510 return SMBTransaction{
511 id: 0,
512 vercmd: SMBVerCmdStat::new(),
513 hdr: SMBCommonHdr::init(),
514 request_done: false,
515 response_done: false,
516 type_data: None,
517 detect_flags_ts: 0,
518 detect_flags_tc: 0,
519 logged: LoggerFlags::new(),
520 de_state: None,
521 events: std::ptr::null_mut(),
522 }
523 }
524
525 pub fn set_status(&mut self, status: u32, is_dos_error: bool)
526 {
527 if is_dos_error {
528 self.vercmd.set_status_dos_error(status);
529 } else {
530 self.vercmd.set_ntstatus(status);
531 }
532 }
533
534 pub fn free(&mut self) {
535 if self.events != std::ptr::null_mut() {
536 sc_app_layer_decoder_events_free_events(&mut self.events);
537 }
538 match self.de_state {
539 Some(state) => {
540 sc_detect_engine_state_free(state);
541 }
542 _ => {}
543 }
544 }
545}
546
547impl Drop for SMBTransaction {
548 fn drop(&mut self) {
549 self.free();
550 }
551}
552
553#[derive(Hash, Eq, PartialEq, Debug, Clone)]
554pub struct SMBFileGUIDOffset {
555 pub guid: Vec<u8>,
556 pub offset: u64,
557}
558
559impl SMBFileGUIDOffset {
560 pub fn new(guid: Vec<u8>, offset: u64) -> SMBFileGUIDOffset {
561 SMBFileGUIDOffset {
562 guid:guid,
563 offset:offset,
564 }
565 }
566}
567
568/// type values to make sure we're not mixing things
569/// up in hashmap lookups
570pub const SMBHDR_TYPE_GUID: u32 = 1;
571pub const SMBHDR_TYPE_SHARE: u32 = 2;
572pub const SMBHDR_TYPE_FILENAME: u32 = 3;
573pub const SMBHDR_TYPE_OFFSET: u32 = 4;
574pub const SMBHDR_TYPE_GENERICTX: u32 = 5;
575pub const SMBHDR_TYPE_HEADER: u32 = 6;
576pub const SMBHDR_TYPE_MAX_SIZE: u32 = 7; // max resp size for SMB1_COMMAND_TRANS
577pub const SMBHDR_TYPE_TXNAME: u32 = 8; // SMB1_COMMAND_TRANS tx_name
578pub const SMBHDR_TYPE_TRANS_FRAG: u32 = 9;
579pub const SMBHDR_TYPE_TREE: u32 = 10;
580pub const SMBHDR_TYPE_DCERPCTX: u32 = 11;
581
582#[derive(Hash, Eq, PartialEq, Debug)]
583pub struct SMBCommonHdr {
584 pub ssn_id: u64,
585 pub tree_id: u32,
586 pub rec_type: u32,
587 pub msg_id: u64,
588}
589
590impl SMBCommonHdr {
591 pub fn init() -> SMBCommonHdr {
592 SMBCommonHdr {
593 rec_type : 0,
594 ssn_id : 0,
595 tree_id : 0,
596 msg_id : 0,
597 }
598 }
599 pub fn new(rec_type: u32, ssn_id: u64, tree_id: u32, msg_id: u64) -> SMBCommonHdr {
600 SMBCommonHdr {
601 rec_type : rec_type,
602 ssn_id : ssn_id,
603 tree_id : tree_id,
604 msg_id : msg_id,
605 }
606 }
607 pub fn from2(r: &Smb2Record, rec_type: u32) -> SMBCommonHdr {
608 let tree_id = match rec_type {
609 SMBHDR_TYPE_TREE => { 0 },
610 _ => r.tree_id,
611 };
612 let msg_id = match rec_type {
be615c9f 613 SMBHDR_TYPE_TRANS_FRAG | SMBHDR_TYPE_SHARE => { 0 },
75d7c9d6
VJ
614 _ => { r.message_id as u64 },
615 };
616
617 SMBCommonHdr {
618 rec_type : rec_type,
619 ssn_id : r.session_id,
620 tree_id : tree_id,
621 msg_id : msg_id,
622 }
623
624 }
625 pub fn from1(r: &SmbRecord, rec_type: u32) -> SMBCommonHdr {
626 let tree_id = match rec_type {
627 SMBHDR_TYPE_TREE => { 0 },
628 _ => r.tree_id as u32,
629 };
630 let msg_id = match rec_type {
be615c9f 631 SMBHDR_TYPE_TRANS_FRAG | SMBHDR_TYPE_SHARE => { 0 },
75d7c9d6
VJ
632 _ => { r.multiplex_id as u64 },
633 };
634
635 SMBCommonHdr {
636 rec_type : rec_type,
637 ssn_id : r.ssn_id as u64,
638 tree_id : tree_id,
639 msg_id : msg_id,
640 }
641 }
642}
643
644#[derive(Hash, Eq, PartialEq, Debug)]
645pub struct SMBHashKeyHdrGuid {
646 hdr: SMBCommonHdr,
647 guid: Vec<u8>,
648}
649
650impl SMBHashKeyHdrGuid {
651 pub fn new(hdr: SMBCommonHdr, guid: Vec<u8>) -> SMBHashKeyHdrGuid {
652 SMBHashKeyHdrGuid {
653 hdr: hdr, guid: guid,
654 }
655 }
656}
657
658#[derive(Hash, Eq, PartialEq, Debug)]
659pub struct SMBTree {
660 pub name: Vec<u8>,
661 pub is_pipe: bool,
662}
663
664impl SMBTree {
665 pub fn new(name: Vec<u8>, is_pipe: bool) -> SMBTree {
666 SMBTree {
667 name:name,
668 is_pipe:is_pipe,
669 }
670 }
671}
672
673pub fn u32_as_bytes(i: u32) -> [u8;4] {
674 let o1: u8 = ((i >> 24) & 0xff) as u8;
675 let o2: u8 = ((i >> 16) & 0xff) as u8;
676 let o3: u8 = ((i >> 8) & 0xff) as u8;
677 let o4: u8 = (i & 0xff) as u8;
678 return [o1, o2, o3, o4]
679}
680
681pub struct SMBState<> {
682 /// map ssn/tree/msgid to vec (guid/name/share)
683 pub ssn2vec_map: HashMap<SMBCommonHdr, Vec<u8>>,
684 /// map guid to filename
685 pub guid2name_map: HashMap<Vec<u8>, Vec<u8>>,
686 /// map ssn key to read offset
687 pub ssn2vecoffset_map: HashMap<SMBCommonHdr, SMBFileGUIDOffset>,
688
689 pub ssn2tree_map: HashMap<SMBCommonHdr, SMBTree>,
690
691 // track the max size we expect for TRANS responses
692 pub ssn2maxsize_map: HashMap<SMBCommonHdr, u16>,
693 pub ssnguid2vec_map: HashMap<SMBHashKeyHdrGuid, Vec<u8>>,
694
695 /// TCP segments defragmentation buffer
696 pub tcp_buffer_ts: Vec<u8>,
697 pub tcp_buffer_tc: Vec<u8>,
698
699 pub files: SMBFiles,
700
701 pub skip_ts: u32,
702 pub skip_tc: u32,
703
704 pub file_ts_left : u32,
705 pub file_tc_left : u32,
706 pub file_ts_guid : Vec<u8>,
707 pub file_tc_guid : Vec<u8>,
708
709 pub ts_ssn_gap: bool,
710 pub tc_ssn_gap: bool,
711
712 pub ts_gap: bool, // last TS update was gap
713 pub tc_gap: bool, // last TC update was gap
714
8bef1208
VJ
715 pub ts_trunc: bool, // no more data for TOSERVER
716 pub tc_trunc: bool, // no more data for TOCLIENT
75d7c9d6
VJ
717
718 /// transactions list
719 pub transactions: Vec<SMBTransaction>,
720
721 /// tx counter for assigning incrementing id's to tx's
722 tx_id: u64,
723
0d69e7b8 724 /// SMB2 dialect or 0 if not set or SMB1
75d7c9d6 725 pub dialect: u16,
0d69e7b8 726 /// contains name of SMB1 dialect
75d7c9d6 727 pub dialect_vec: Option<Vec<u8>>, // used if dialect == 0
75d7c9d6
VJ
728
729 /// dcerpc interfaces, stored here to be able to match
730 /// them while inspecting DCERPC REQUEST txs
731 pub dcerpc_ifaces: Option<Vec<DCERPCIface>>,
75d7c9d6
VJ
732}
733
734impl SMBState {
735 /// Allocation function for a new TLS parser instance
736 pub fn new() -> SMBState {
737 SMBState {
738 ssn2vec_map:HashMap::new(),
739 guid2name_map:HashMap::new(),
740 ssn2vecoffset_map:HashMap::new(),
741 ssn2tree_map:HashMap::new(),
742 ssn2maxsize_map:HashMap::new(),
743 ssnguid2vec_map:HashMap::new(),
744 tcp_buffer_ts:Vec::new(),
745 tcp_buffer_tc:Vec::new(),
746 files: SMBFiles::new(),
747 skip_ts:0,
748 skip_tc:0,
749 file_ts_left:0,
750 file_tc_left:0,
751 file_ts_guid:Vec::new(),
752 file_tc_guid:Vec::new(),
753 ts_ssn_gap: false,
754 tc_ssn_gap: false,
755 ts_gap: false,
756 tc_gap: false,
757 ts_trunc: false,
758 tc_trunc: false,
759 transactions: Vec::new(),
760 tx_id:0,
761 dialect:0,
762 dialect_vec: None,
75d7c9d6 763 dcerpc_ifaces: None,
75d7c9d6
VJ
764 }
765 }
766
767 pub fn free(&mut self) {
768 //self._debug_state_stats();
769 self._debug_tx_stats();
770 self.files.free();
771 }
772
773 pub fn new_tx(&mut self) -> SMBTransaction {
774 let mut tx = SMBTransaction::new();
775 self.tx_id += 1;
776 tx.id = self.tx_id;
777 SCLogDebug!("TX {} created", tx.id);
778 return tx;
779 }
780
781 pub fn free_tx(&mut self, tx_id: u64) {
782 SCLogDebug!("Freeing TX with ID {} TX.ID {}", tx_id, tx_id+1);
783 let len = self.transactions.len();
784 let mut found = false;
785 let mut index = 0;
786 for i in 0..len {
787 let tx = &self.transactions[i];
788 if tx.id == tx_id + 1 {
789 found = true;
790 index = i;
791 SCLogDebug!("tx {} progress {}/{}", tx.id, tx.request_done, tx.response_done);
792 break;
793 }
794 }
795 if found {
796 SCLogDebug!("freeing TX with ID {} TX.ID {} at index {} left: {} max id: {}",
797 tx_id, tx_id+1, index, self.transactions.len(), self.tx_id);
798 self.transactions.remove(index);
799 }
800 }
801
802 // for use with the C API call StateGetTxIterator
803 pub fn get_tx_iterator(&mut self, min_tx_id: u64, state: &mut u64) ->
804 Option<(&SMBTransaction, u64, bool)>
805 {
806 let mut index = *state as usize;
807 let len = self.transactions.len();
808
809 // find tx that is >= min_tx_id
810 while index < len {
811 let tx = &self.transactions[index];
812 if tx.id < min_tx_id + 1 {
813 index += 1;
814 continue;
815 }
816 *state = index as u64 + 1;
817 //SCLogDebug!("returning tx_id {} has_next? {} (len {} index {}), tx {:?}",
818 // tx.id - 1, (len - index) > 1, len, index, tx);
819 return Some((tx, tx.id - 1, (len - index) > 1));
820 }
821 return None;
822 }
823
824 pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SMBTransaction> {
825/*
826 if self.transactions.len() > 100 {
827 SCLogNotice!("get_tx_by_id: tx_id={} in list={}", tx_id, self.transactions.len());
828 self._dump_txs();
829 panic!("txs exploded");
830 }
831*/
832 for tx in &mut self.transactions {
833 if tx.id == tx_id + 1 {
834 let ver = tx.vercmd.get_version();
835 let mut _smbcmd;
836 if ver == 2 {
837 let (_, cmd) = tx.vercmd.get_smb2_cmd();
838 _smbcmd = cmd;
839 } else {
840 let (_, cmd) = tx.vercmd.get_smb1_cmd();
841 _smbcmd = cmd as u16;
842 }
843 SCLogDebug!("Found SMB TX: id {} ver:{} cmd:{} progress {}/{} type_data {:?}",
844 tx.id, ver, _smbcmd, tx.request_done, tx.response_done, tx.type_data);
845 return Some(tx);
846 }
847 }
848 SCLogDebug!("Failed to find SMB TX with ID {}", tx_id);
849 return None;
850 }
851
852 /* generic TX has no type_data and is only used to
853 * track a single cmd request/reply pair. */
854
855 pub fn new_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: SMBCommonHdr)
856 -> (&mut SMBTransaction)
857 {
858 let mut tx = self.new_tx();
859 if smb_ver == 1 && smb_cmd <= 255 {
860 tx.vercmd.set_smb1_cmd(smb_cmd as u8);
861 } else if smb_ver == 2 {
862 tx.vercmd.set_smb2_cmd(smb_cmd);
863 }
864
865 tx.type_data = None;
866 tx.request_done = true;
867 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
868 tx.hdr = key;
869
870 SCLogDebug!("SMB: TX GENERIC created: ID {} tx list {} {:?}",
871 tx.id, self.transactions.len(), &tx);
872 self.transactions.push(tx);
873 let tx_ref = self.transactions.last_mut();
874 return tx_ref.unwrap();
875 }
876
877 pub fn get_last_tx(&mut self, smb_ver: u8, smb_cmd: u16)
878 -> Option<&mut SMBTransaction>
879 {
880 let tx_ref = self.transactions.last_mut();
881 match tx_ref {
882 Some(tx) => {
883 let found = if tx.vercmd.get_version() == smb_ver {
884 if smb_ver == 1 {
885 let (_, cmd) = tx.vercmd.get_smb1_cmd();
886 cmd as u16 == smb_cmd
887 } else if smb_ver == 2 {
888 let (_, cmd) = tx.vercmd.get_smb2_cmd();
889 cmd == smb_cmd
890 } else {
891 false
892 }
893 } else {
894 false
895 };
896 if found {
897 return Some(tx);
898 }
899 },
900 None => { },
901 }
902 return None;
903 }
904
905 pub fn get_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: &SMBCommonHdr)
906 -> Option<&mut SMBTransaction>
907 {
908 for tx in &mut self.transactions {
909 let found = if tx.vercmd.get_version() == smb_ver {
910 if smb_ver == 1 {
911 let (_, cmd) = tx.vercmd.get_smb1_cmd();
912 cmd as u16 == smb_cmd && tx.hdr == *key
913 } else if smb_ver == 2 {
914 let (_, cmd) = tx.vercmd.get_smb2_cmd();
915 cmd == smb_cmd && tx.hdr == *key
916 } else {
917 false
918 }
919 } else {
920 false
921 };
922 if found {
923 return Some(tx);
924 }
925 }
926 return None;
927 }
928
929 pub fn new_negotiate_tx(&mut self, smb_ver: u8)
930 -> (&mut SMBTransaction)
931 {
932 let mut tx = self.new_tx();
933 if smb_ver == 1 {
934 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_NEGOTIATE_PROTOCOL);
935 } else if smb_ver == 2 {
936 tx.vercmd.set_smb2_cmd(SMB2_COMMAND_NEGOTIATE_PROTOCOL);
937 }
938
939 tx.type_data = Some(SMBTransactionTypeData::NEGOTIATE(
940 SMBTransactionNegotiate::new(smb_ver)));
941 tx.request_done = true;
942 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
943
944 SCLogDebug!("SMB: TX NEGOTIATE created: ID {} SMB ver {}", tx.id, smb_ver);
945 self.transactions.push(tx);
946 let tx_ref = self.transactions.last_mut();
947 return tx_ref.unwrap();
948 }
949
950 pub fn get_negotiate_tx(&mut self, smb_ver: u8)
951 -> Option<&mut SMBTransaction>
952 {
953 for tx in &mut self.transactions {
954 let found = match tx.type_data {
955 Some(SMBTransactionTypeData::NEGOTIATE(ref x)) => {
956 if x.smb_ver == smb_ver {
957 true
958 } else {
959 false
960 }
961 },
962 _ => { false },
963 };
964 if found {
965 return Some(tx);
966 }
967 }
968 return None;
969 }
970
971 pub fn new_treeconnect_tx(&mut self, hdr: SMBCommonHdr, name: Vec<u8>)
972 -> (&mut SMBTransaction)
973 {
974 let mut tx = self.new_tx();
975
976 tx.hdr = hdr;
977 tx.type_data = Some(SMBTransactionTypeData::TREECONNECT(
978 SMBTransactionTreeConnect::new(name.to_vec())));
979 tx.request_done = true;
980 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
981
982 SCLogDebug!("SMB: TX TREECONNECT created: ID {} NAME {}",
983 tx.id, String::from_utf8_lossy(&name));
984 self.transactions.push(tx);
985 let tx_ref = self.transactions.last_mut();
986 return tx_ref.unwrap();
987 }
988
989 pub fn get_treeconnect_tx(&mut self, hdr: SMBCommonHdr)
990 -> Option<&mut SMBTransaction>
991 {
992 for tx in &mut self.transactions {
993 let hit = tx.hdr == hdr && match tx.type_data {
994 Some(SMBTransactionTypeData::TREECONNECT(_)) => { true },
995 _ => { false },
996 };
997 if hit {
998 return Some(tx);
999 }
1000 }
1001 return None;
1002 }
1003
1004 pub fn new_create_tx(&mut self, file_name: &Vec<u8>,
1005 disposition: u32, del: bool, dir: bool,
1006 hdr: SMBCommonHdr)
1007 -> &mut SMBTransaction
1008 {
1009 let mut tx = self.new_tx();
1010 tx.hdr = hdr;
1011 tx.type_data = Some(SMBTransactionTypeData::CREATE(
1012 SMBTransactionCreate::new(
1013 file_name.to_vec(), disposition,
1014 del, dir)));
1015 tx.request_done = true;
1016 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
1017
1018 self.transactions.push(tx);
1019 let tx_ref = self.transactions.last_mut();
1020 return tx_ref.unwrap();
1021 }
1022
1023 pub fn get_create_tx_by_hdr(&mut self, hdr: &SMBCommonHdr)
1024 -> Option<&mut SMBTransaction>
1025 {
1026 for tx in &mut self.transactions {
1027 let found = match tx.type_data {
1028 Some(SMBTransactionTypeData::CREATE(ref _d)) => {
1029 *hdr == tx.hdr
1030 },
1031 _ => { false },
1032 };
1033
1034 if found {
1035 SCLogDebug!("SMB: Found SMB create TX with ID {}", tx.id);
1036 return Some(tx);
1037 }
1038 }
1039 SCLogDebug!("SMB: Failed to find SMB create TX with key {:?}", hdr);
1040 return None;
1041 }
1042
1043 pub fn get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool)
1044 {
1045 let (name, is_dcerpc) = match self.guid2name_map.get(&guid.to_vec()) {
1046 Some(n) => {
1047 match str::from_utf8(&n) {
1048 Ok("PSEXESVC") => ("PSEXESVC", false),
1049 Ok("svcctl") => ("svcctl", true),
1050 Ok("srvsvc") => ("srvsvc", true),
1051 Ok("atsvc") => ("atsvc", true),
1052 Ok("lsarpc") => ("lsarpc", true),
1053 Ok("samr") => ("samr", true),
1054 Err(_) => ("MALFORMED", false),
1055 Ok(&_) => {
ea1e13cb 1056 SCLogDebug!("don't know {}", String::from_utf8_lossy(&n));
75d7c9d6
VJ
1057 ("UNKNOWN", false)
1058 },
1059 }
1060 },
1061 _ => { ("UNKNOWN", false) },
1062 };
1063 SCLogDebug!("service {} is_dcerpc {}", name, is_dcerpc);
1064 (&name, is_dcerpc)
1065 }
1066
1067 /* if we have marked the ssn as 'gapped' we check to see if
1068 * we've caught up. The check is to see if we have the last
a44504a1
VJ
1069 * tx in our list is done. This means we've seen both sides
1070 * and we're back in sync. Mark older txs as 'done' */
1071 fn check_gap_resync(&mut self, prior_max_id: u64)
75d7c9d6 1072 {
a44504a1
VJ
1073 SCLogDebug!("check_gap_resync2: post-GAP resync check ({}/{})", self.ts_ssn_gap, self.tc_ssn_gap);
1074 if !self.ts_ssn_gap && !self.tc_ssn_gap {
1075 return;
1076 }
1077
1078 let (last_done, id) = match self.transactions.last() {
1079 Some(tx) => {
1080 (tx.request_done && tx.response_done, tx.id)
1081 },
1082 None => (false, 0),
1083 };
1084 if last_done && id > 0 {
ea1e13cb 1085 SCLogDebug!("check_gap_resync2: TX {} is done post-GAP, mark all older ones complete", id);
a44504a1
VJ
1086 self.ts_ssn_gap = false;
1087 self.tc_ssn_gap = false;
1088 self.close_non_file_txs(prior_max_id);
75d7c9d6
VJ
1089 }
1090 }
1091
1092 /* close all txs execpt file xfers. */
a44504a1
VJ
1093 fn close_non_file_txs(&mut self, max_id: u64) {
1094 SCLogDebug!("close_non_file_txs: checking for non-file txs to wrap up");
75d7c9d6 1095 for tx in &mut self.transactions {
a44504a1
VJ
1096 if tx.id >= max_id {
1097 SCLogDebug!("close_non_file_txs: done");
1098 break;
1099 }
1100 if let Some(SMBTransactionTypeData::FILE(_)) = tx.type_data {
1101 // leaving FILE txs open as they can deal with gaps.
1102 } else {
1103 SCLogDebug!("ose_non_file_txs: tx {} marked as done", tx.id);
1104 tx.request_done = true;
1105 tx.response_done = true;
75d7c9d6
VJ
1106 }
1107 }
1108 }
1109
1110 // return how much data we consumed
1111 fn handle_skip(&mut self, direction: u8, input_size: u32) -> u32 {
1112 let mut skip_left = if direction == STREAM_TOSERVER {
1113 self.skip_ts
1114 } else {
1115 self.skip_tc
1116 };
1117 if skip_left == 0 {
1118 return 0
1119 }
1120 SCLogDebug!("skip_left {} input_size {}", skip_left, input_size);
1121
1122 let consumed = if skip_left >= input_size {
1123 input_size
1124 } else {
1125 skip_left
1126 };
1127
1128 if skip_left <= input_size {
1129 skip_left = 0;
1130 } else {
1131 skip_left -= input_size;
1132 }
1133
1134 if direction == STREAM_TOSERVER {
1135 self.skip_ts = skip_left;
1136 } else {
1137 self.skip_tc = skip_left;
1138 }
1139 return consumed;
1140 }
1141
1142 /// return bytes consumed
1143 pub fn parse_tcp_data_ts_partial<'b>(&mut self, input: &'b[u8]) -> usize
1144 {
1145 SCLogDebug!("incomplete of size {}", input.len());
1146 if input.len() < 512 {
7dff9b99
VJ
1147 // check for malformed data. Wireshark reports as
1148 // 'NBSS continuation data'. If it's invalid we're
1149 // lost so we give up.
1150 if input.len() > 8 {
1151 match parse_nbss_record_partial(input) {
1152 IResult::Done(_, ref hdr) => {
1153 if !hdr.is_smb() {
1154 SCLogDebug!("partial NBSS, not SMB and no known msg type {}", hdr.message_type);
1155 self.trunc_ts();
1156 return 0;
1157 }
1158 },
1159 _ => {},
1160 }
1161 }
75d7c9d6
VJ
1162 return 0;
1163 }
1164
1165 match parse_nbss_record_partial(input) {
1166 IResult::Done(output, ref nbss_part_hdr) => {
1167 SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len());
1168 if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1169 match parse_smb_version(&nbss_part_hdr.data) {
1170 IResult::Done(_, ref smb) => {
1171 SCLogDebug!("SMB {:?}", smb);
b3439205 1172 if smb.version == 0xff_u8 { // SMB1
75d7c9d6
VJ
1173 SCLogDebug!("SMBv1 record");
1174 match parse_smb_record(&nbss_part_hdr.data) {
1175 IResult::Done(_, ref r) => {
1176 if r.command == SMB1_COMMAND_WRITE_ANDX {
1177 // see if it's a write to a pipe. We only handle those
1178 // if complete.
1179 let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
1180 r.ssn_id as u64, r.tree_id as u32, 0);
1181 let is_pipe = match self.ssn2tree_map.get(&tree_key) {
1182 Some(n) => n.is_pipe,
1183 None => false,
1184 };
1185 if is_pipe {
1186 return 0;
1187 }
1188 smb1_write_request_record(self, r);
1189 let consumed = input.len() - output.len();
1190 return consumed;
1191 }
1192 },
1193 _ => { },
1194
1195 }
b3439205 1196 } else if smb.version == 0xfe_u8 { // SMB2
75d7c9d6
VJ
1197 SCLogDebug!("SMBv2 record");
1198 match parse_smb2_request_record(&nbss_part_hdr.data) {
1199 IResult::Done(_, ref smb_record) => {
1200 SCLogDebug!("SMB2: partial record {}",
1201 &smb2_command_string(smb_record.command));
1202 if smb_record.command == SMB2_COMMAND_WRITE {
1203 smb2_write_request_record(self, smb_record);
1204 let consumed = input.len() - output.len();
1205 return consumed;
1206 }
1207 },
1208 _ => { },
1209 }
1210 }
b3439205 1211 // no SMB3 here yet, will buffer full records
75d7c9d6
VJ
1212 },
1213 _ => { },
1214 }
1215 }
1216 },
1217 _ => { },
1218 }
1219
1220 return 0;
1221 }
1222
1223 /// Parsing function, handling TCP chunks fragmentation
1224 pub fn parse_tcp_data_ts<'b>(&mut self, i: &'b[u8]) -> u32
1225 {
a44504a1 1226 let max_tx_id = self.tx_id;
75d7c9d6
VJ
1227
1228 let mut v : Vec<u8>;
1229 //println!("parse_tcp_data_ts ({})",i.len());
1230 //println!("{:?}",i);
1231 // Check if TCP data is being defragmented
1232 let tcp_buffer = match self.tcp_buffer_ts.len() {
1233 0 => i,
1234 _ => {
1235 v = self.tcp_buffer_ts.split_off(0);
1236 if self.tcp_buffer_ts.len() + i.len() > 100000 {
1237 self.set_event(SMBEvent::RecordOverflow);
1238 return 1;
1239 };
1240 v.extend_from_slice(i);
1241 v.as_slice()
1242 },
1243 };
1244 //println!("tcp_buffer ({})",tcp_buffer.len());
1245 let mut cur_i = tcp_buffer;
1246 if cur_i.len() > 1000000 {
1247 self.set_event(SMBEvent::RecordOverflow);
1248 return 1;
1249 }
1250 let consumed = self.handle_skip(STREAM_TOSERVER, cur_i.len() as u32);
1251 if consumed > 0 {
1252 if consumed > cur_i.len() as u32 {
1253 self.set_event(SMBEvent::InternalError);
1254 return 1;
1255 }
1256 cur_i = &cur_i[consumed as usize..];
1257 }
1258 // take care of in progress file chunk transfers
1259 // and skip buffer beyond it
1260 let consumed = self.filetracker_update(STREAM_TOSERVER, cur_i, 0);
1261 if consumed > 0 {
1262 if consumed > cur_i.len() as u32 {
1263 self.set_event(SMBEvent::InternalError);
1264 return 1;
1265 }
1266 cur_i = &cur_i[consumed as usize..];
1267 }
1268 // gap
1269 if self.ts_gap {
a44504a1 1270 SCLogDebug!("TS trying to catch up after GAP (input {})", cur_i.len());
b3439205 1271 match search_smb_record(cur_i) {
75d7c9d6
VJ
1272 IResult::Done(_, pg) => {
1273 SCLogDebug!("smb record found");
1274 let smb2_offset = cur_i.len() - pg.data.len();
1275 if smb2_offset < 4 {
1276 return 0;
1277 }
1278 let nbss_offset = smb2_offset - 4;
1279 cur_i = &cur_i[nbss_offset..];
1280
1281 self.ts_gap = false;
1282 },
1283 _ => {
1284 SCLogDebug!("smb record NOT found");
1285 self.tcp_buffer_ts.extend_from_slice(cur_i);
1286 return 0;
1287 },
1288 }
1289 }
1290 while cur_i.len() > 0 { // min record size
1291 match parse_nbss_record(cur_i) {
1292 IResult::Done(rem, ref nbss_hdr) => {
1293 if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1294 // we have the full records size worth of data,
1295 // let's parse it
1296 match parse_smb_version(&nbss_hdr.data) {
1297 IResult::Done(_, ref smb) => {
1298 SCLogDebug!("SMB {:?}", smb);
b3439205 1299 if smb.version == 0xff_u8 { // SMB1
75d7c9d6
VJ
1300 SCLogDebug!("SMBv1 record");
1301 match parse_smb_record(&nbss_hdr.data) {
1302 IResult::Done(_, ref smb_record) => {
1303 smb1_request_record(self, smb_record);
1304 },
1305 _ => {
1306 self.set_event(SMBEvent::MalformedData);
1307 return 1;
1308 },
1309 }
b3439205 1310 } else if smb.version == 0xfe_u8 { // SMB2
75d7c9d6
VJ
1311 let mut nbss_data = nbss_hdr.data;
1312 while nbss_data.len() > 0 {
1313 SCLogDebug!("SMBv2 record");
1314 match parse_smb2_request_record(&nbss_data) {
1315 IResult::Done(nbss_data_rem, ref smb_record) => {
1316 SCLogDebug!("nbss_data_rem {}", nbss_data_rem.len());
1317
1318 smb2_request_record(self, smb_record);
1319 nbss_data = nbss_data_rem;
1320 },
1321 _ => {
1322 self.set_event(SMBEvent::MalformedData);
1323 return 1;
1324 },
1325 }
1326 }
b3439205
VJ
1327 } else if smb.version == 0xfd_u8 { // SMB3 transform
1328 let mut nbss_data = nbss_hdr.data;
1329 while nbss_data.len() > 0 {
1330 SCLogDebug!("SMBv3 transform record");
1331 match parse_smb3_transform_record(&nbss_data) {
1332 IResult::Done(nbss_data_rem, ref _smb3_record) => {
1333 nbss_data = nbss_data_rem;
1334 },
1335 _ => {
1336 self.set_event(SMBEvent::MalformedData);
1337 return 1;
1338 },
1339 }
1340 }
75d7c9d6
VJ
1341 }
1342 },
1343 _ => {
1344 self.set_event(SMBEvent::MalformedData);
1345 return 1;
1346 },
1347 }
1348 } else {
1349 SCLogDebug!("NBSS message {:X}", nbss_hdr.message_type);
1350 }
1351 cur_i = rem;
1352 },
1353 IResult::Incomplete(_) => {
1354 let consumed = self.parse_tcp_data_ts_partial(cur_i);
1355 cur_i = &cur_i[consumed ..];
1356
1357 self.tcp_buffer_ts.extend_from_slice(cur_i);
1358 break;
1359 },
1360 IResult::Error(_) => {
1361 self.set_event(SMBEvent::MalformedData);
1362 return 1;
1363 },
1364 }
1365 };
1366
a44504a1 1367 self.check_gap_resync(max_tx_id);
75d7c9d6
VJ
1368 0
1369 }
1370
1371 /// return bytes consumed
1372 pub fn parse_tcp_data_tc_partial<'b>(&mut self, input: &'b[u8]) -> usize
1373 {
1374 SCLogDebug!("incomplete of size {}", input.len());
1375 if input.len() < 512 {
7dff9b99
VJ
1376 // check for malformed data. Wireshark reports as
1377 // 'NBSS continuation data'. If it's invalid we're
1378 // lost so we give up.
1379 if input.len() > 8 {
1380 match parse_nbss_record_partial(input) {
1381 IResult::Done(_, ref hdr) => {
1382 if !hdr.is_smb() {
1383 SCLogDebug!("partial NBSS, not SMB and no known msg type {}", hdr.message_type);
1384 self.trunc_tc();
1385 return 0;
1386 }
1387 },
1388 _ => {},
1389 }
1390 }
75d7c9d6
VJ
1391 return 0;
1392 }
1393
1394 match parse_nbss_record_partial(input) {
1395 IResult::Done(output, ref nbss_part_hdr) => {
1396 SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len());
1397 if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1398 match parse_smb_version(&nbss_part_hdr.data) {
1399 IResult::Done(_, ref smb) => {
1400 SCLogDebug!("SMB {:?}", smb);
1401 if smb.version == 255u8 { // SMB1
1402 SCLogDebug!("SMBv1 record");
1403 match parse_smb_record(&nbss_part_hdr.data) {
1404 IResult::Done(_, ref r) => {
1405 SCLogDebug!("SMB1: partial record {}",
1406 r.command);
1407 if r.command == SMB1_COMMAND_READ_ANDX {
1408 let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
1409 r.ssn_id as u64, r.tree_id as u32, 0);
1410 let is_pipe = match self.ssn2tree_map.get(&tree_key) {
1411 Some(n) => n.is_pipe,
1412 None => false,
1413 };
1414 if is_pipe {
1415 return 0;
1416 }
1417 smb1_read_response_record(self, r);
1418 let consumed = input.len() - output.len();
1419 return consumed;
1420 }
1421 },
1422 _ => { },
1423 }
1424 } else if smb.version == 254u8 { // SMB2
1425 SCLogDebug!("SMBv2 record");
1426 match parse_smb2_response_record(&nbss_part_hdr.data) {
1427 IResult::Done(_, ref smb_record) => {
1428 SCLogDebug!("SMB2: partial record {}",
1429 &smb2_command_string(smb_record.command));
1430 if smb_record.command == SMB2_COMMAND_READ {
1431 smb2_read_response_record(self, smb_record);
1432 let consumed = input.len() - output.len();
1433 return consumed;
1434 }
1435 },
1436 _ => { },
1437 }
1438 }
b3439205 1439 // no SMB3 here yet, will buffer full records
75d7c9d6
VJ
1440 },
1441 _ => { },
1442 }
1443 }
1444 },
1445 _ => { },
1446 }
1447
1448 return 0;
1449 }
1450
1451 /// Parsing function, handling TCP chunks fragmentation
1452 pub fn parse_tcp_data_tc<'b>(&mut self, i: &'b[u8]) -> u32
1453 {
a44504a1 1454 let max_tx_id = self.tx_id;
75d7c9d6
VJ
1455
1456 let mut v : Vec<u8>;
1457 // Check if TCP data is being defragmented
1458 let tcp_buffer = match self.tcp_buffer_tc.len() {
1459 0 => i,
1460 _ => {
1461 v = self.tcp_buffer_tc.split_off(0);
1462 if self.tcp_buffer_tc.len() + i.len() > 100000 {
1463 self.set_event(SMBEvent::RecordOverflow);
1464 return 1;
1465 };
1466 v.extend_from_slice(i);
1467 v.as_slice()
1468 },
1469 };
1470 let mut cur_i = tcp_buffer;
1471 SCLogDebug!("cur_i.len {}", cur_i.len());
1472 if cur_i.len() > 100000 {
1473 self.set_event(SMBEvent::RecordOverflow);
1474 return 1;
1475 }
1476 let consumed = self.handle_skip(STREAM_TOCLIENT, cur_i.len() as u32);
1477 if consumed > 0 {
1478 if consumed > cur_i.len() as u32 {
1479 self.set_event(SMBEvent::InternalError);
1480 return 1;
1481 }
1482 cur_i = &cur_i[consumed as usize..];
1483 }
1484 // take care of in progress file chunk transfers
1485 // and skip buffer beyond it
1486 let consumed = self.filetracker_update(STREAM_TOCLIENT, cur_i, 0);
1487 if consumed > 0 {
1488 if consumed > cur_i.len() as u32 {
1489 self.set_event(SMBEvent::InternalError);
1490 return 1;
1491 }
1492 cur_i = &cur_i[consumed as usize..];
1493 }
1494 // gap
1495 if self.tc_gap {
b3439205
VJ
1496 SCLogDebug!("TC trying to catch up after GAP (input {})", cur_i.len());
1497 match search_smb_record(cur_i) {
75d7c9d6
VJ
1498 IResult::Done(_, pg) => {
1499 SCLogDebug!("smb record found");
1500 let smb2_offset = cur_i.len() - pg.data.len();
1501 if smb2_offset < 4 {
1502 return 0;
1503 }
1504 let nbss_offset = smb2_offset - 4;
1505 cur_i = &cur_i[nbss_offset..];
1506
1507 self.tc_gap = false;
1508 },
1509 _ => {
1510 SCLogDebug!("smb record NOT found");
1511 self.tcp_buffer_tc.extend_from_slice(cur_i);
1512 return 0;
1513 },
1514 }
1515 }
1516 while cur_i.len() > 0 { // min record size
1517 match parse_nbss_record(cur_i) {
1518 IResult::Done(rem, ref nbss_hdr) => {
1519 if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1520 // we have the full records size worth of data,
1521 // let's parse it
1522 match parse_smb_version(&nbss_hdr.data) {
1523 IResult::Done(_, ref smb) => {
1524 SCLogDebug!("SMB {:?}", smb);
b3439205 1525 if smb.version == 0xff_u8 { // SMB1
75d7c9d6
VJ
1526 SCLogDebug!("SMBv1 record");
1527 match parse_smb_record(&nbss_hdr.data) {
1528 IResult::Done(_, ref smb_record) => {
1529 smb1_response_record(self, smb_record);
1530 },
1531 _ => {
1532 self.set_event(SMBEvent::MalformedData);
1533 return 1;
1534 },
1535 }
b3439205 1536 } else if smb.version == 0xfe_u8 { // SMB2
75d7c9d6
VJ
1537 let mut nbss_data = nbss_hdr.data;
1538 while nbss_data.len() > 0 {
1539 SCLogDebug!("SMBv2 record");
1540 match parse_smb2_response_record(&nbss_data) {
1541 IResult::Done(nbss_data_rem, ref smb_record) => {
1542 smb2_response_record(self, smb_record);
1543 nbss_data = nbss_data_rem;
b3439205
VJ
1544 },
1545 _ => {
1546 self.set_event(SMBEvent::MalformedData);
1547 return 1;
1548 },
1549 }
1550 }
1551 } else if smb.version == 0xfd_u8 { // SMB3 transform
1552 let mut nbss_data = nbss_hdr.data;
1553 while nbss_data.len() > 0 {
1554 SCLogDebug!("SMBv3 transform record");
1555 match parse_smb3_transform_record(&nbss_data) {
1556 IResult::Done(nbss_data_rem, ref _smb3_record) => {
1557 nbss_data = nbss_data_rem;
75d7c9d6
VJ
1558 },
1559 _ => {
1560 self.set_event(SMBEvent::MalformedData);
1561 return 1;
1562 },
1563 }
1564 }
1565 }
1566 },
1567 IResult::Incomplete(_) => {
1568 // not enough data to contain basic SMB hdr
1569 // TODO event: empty NBSS_MSGTYPE_SESSION_MESSAGE
1570 },
1571 IResult::Error(_) => {
1572 self.set_event(SMBEvent::MalformedData);
1573 return 1;
1574 },
1575 }
1576 } else {
1577 SCLogDebug!("NBSS message {:X}", nbss_hdr.message_type);
1578 }
1579 cur_i = rem;
1580 },
7dff9b99
VJ
1581 IResult::Incomplete(needed) => {
1582 SCLogDebug!("INCOMPLETE have {} needed {:?}", cur_i.len(), needed);
75d7c9d6
VJ
1583 let consumed = self.parse_tcp_data_tc_partial(cur_i);
1584 cur_i = &cur_i[consumed ..];
1585
1586 SCLogDebug!("INCOMPLETE have {}", cur_i.len());
1587 self.tcp_buffer_tc.extend_from_slice(cur_i);
1588 break;
1589 },
1590 IResult::Error(_) => {
1591 self.set_event(SMBEvent::MalformedData);
1592 return 1;
1593 },
1594 }
1595 };
a44504a1 1596 self.check_gap_resync(max_tx_id);
28f16e38 1597 self._debug_tx_stats();
75d7c9d6
VJ
1598 0
1599 }
1600
1601 /// handle a gap in the TOSERVER direction
1602 /// returns: 0 ok, 1 unrecoverable error
1603 pub fn parse_tcp_data_ts_gap(&mut self, gap_size: u32) -> u32 {
1604 if self.tcp_buffer_ts.len() > 0 {
1605 self.tcp_buffer_ts.clear();
1606 }
1607 let consumed = self.handle_skip(STREAM_TOSERVER, gap_size);
1608 if consumed < gap_size {
1609 let new_gap_size = gap_size - consumed;
1610 let gap = vec![0; new_gap_size as usize];
1611
1612 let consumed2 = self.filetracker_update(STREAM_TOSERVER, &gap, new_gap_size);
1613 if consumed2 > new_gap_size {
1614 SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size);
1615 self.set_event(SMBEvent::InternalError);
1616 return 1;
1617 }
1618 }
1619 SCLogDebug!("GAP of size {} in toserver direction", gap_size);
1620 self.ts_ssn_gap = true;
1621 self.ts_gap = true;
1622 return 0
1623 }
1624
1625 /// handle a gap in the TOCLIENT direction
1626 /// returns: 0 ok, 1 unrecoverable error
1627 pub fn parse_tcp_data_tc_gap(&mut self, gap_size: u32) -> u32 {
1628 if self.tcp_buffer_tc.len() > 0 {
1629 self.tcp_buffer_tc.clear();
1630 }
1631 let consumed = self.handle_skip(STREAM_TOCLIENT, gap_size);
1632 if consumed < gap_size {
1633 let new_gap_size = gap_size - consumed;
1634 let gap = vec![0; new_gap_size as usize];
1635
1636 let consumed2 = self.filetracker_update(STREAM_TOCLIENT, &gap, new_gap_size);
1637 if consumed2 > new_gap_size {
1638 SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size);
1639 self.set_event(SMBEvent::InternalError);
1640 return 1;
1641 }
1642 }
1643 SCLogDebug!("GAP of size {} in toclient direction", gap_size);
1644 self.tc_ssn_gap = true;
1645 self.tc_gap = true;
1646 return 0
1647 }
1648
1649 pub fn trunc_ts(&mut self) {
1650 SCLogDebug!("TRUNC TS");
1651 self.ts_trunc = true;
7dff9b99 1652 self.tcp_buffer_ts.clear();
75d7c9d6
VJ
1653
1654 for tx in &mut self.transactions {
1655 if !tx.request_done {
1656 SCLogDebug!("TRUNCING TX {} in TOSERVER direction", tx.id);
1657 tx.request_done = true;
1658 }
1659 }
1660 }
1661 pub fn trunc_tc(&mut self) {
1662 SCLogDebug!("TRUNC TC");
1663 self.tc_trunc = true;
7dff9b99 1664 self.tcp_buffer_tc.clear();
75d7c9d6
VJ
1665
1666 for tx in &mut self.transactions {
1667 if !tx.response_done {
1668 SCLogDebug!("TRUNCING TX {} in TOCLIENT direction", tx.id);
1669 tx.response_done = true;
1670 }
1671 }
1672 }
1673}
1674
1675/// Returns *mut SMBState
1676#[no_mangle]
1677pub extern "C" fn rs_smb_state_new() -> *mut libc::c_void {
1678 let state = SMBState::new();
1679 let boxed = Box::new(state);
1680 SCLogDebug!("allocating state");
1681 return unsafe{transmute(boxed)};
1682}
1683
1684/// Params:
1685/// - state: *mut SMBState as void pointer
1686#[no_mangle]
1687pub extern "C" fn rs_smb_state_free(state: *mut libc::c_void) {
1688 // Just unbox...
1689 SCLogDebug!("freeing state");
1690 let mut smb_state: Box<SMBState> = unsafe{transmute(state)};
1691 smb_state.free();
1692}
1693
1694/// C binding parse a SMB request. Returns 1 on success, -1 on failure.
1695#[no_mangle]
1696pub extern "C" fn rs_smb_parse_request_tcp(_flow: *mut Flow,
1697 state: &mut SMBState,
1698 _pstate: *mut libc::c_void,
1699 input: *mut libc::uint8_t,
1700 input_len: libc::uint32_t,
1701 _data: *mut libc::c_void)
1702 -> libc::int8_t
1703{
1704 let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
1705 SCLogDebug!("parsing {} bytes of request data", input_len);
1706
1707 if state.parse_tcp_data_ts(buf) == 0 {
1708 return 1;
1709 } else {
1710 return -1;
1711 }
1712}
1713
1714#[no_mangle]
1715pub extern "C" fn rs_smb_parse_request_tcp_gap(
1716 state: &mut SMBState,
1717 input_len: libc::uint32_t)
1718 -> libc::int8_t
1719{
1720 if state.parse_tcp_data_ts_gap(input_len as u32) == 0 {
1721 return 1;
1722 }
1723 return -1;
1724}
1725
1726
1727#[no_mangle]
1728pub extern "C" fn rs_smb_parse_response_tcp(_flow: *mut Flow,
1729 state: &mut SMBState,
1730 _pstate: *mut libc::c_void,
1731 input: *mut libc::uint8_t,
1732 input_len: libc::uint32_t,
1733 _data: *mut libc::c_void)
1734 -> libc::int8_t
1735{
1736 SCLogDebug!("parsing {} bytes of response data", input_len);
1737 let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
1738
1739 if state.parse_tcp_data_tc(buf) == 0 {
1740 return 1;
1741 } else {
1742 return -1;
1743 }
1744}
1745
1746#[no_mangle]
1747pub extern "C" fn rs_smb_parse_response_tcp_gap(
1748 state: &mut SMBState,
1749 input_len: libc::uint32_t)
1750 -> libc::int8_t
1751{
1752 if state.parse_tcp_data_tc_gap(input_len as u32) == 0 {
1753 return 1;
1754 }
1755 return -1;
1756}
1757
75d7c9d6 1758#[no_mangle]
7ab071a5 1759pub extern "C" fn rs_smb_probe_tcp(input: *const libc::uint8_t, len: libc::uint32_t)
75d7c9d6
VJ
1760 -> libc::int8_t
1761{
7ab071a5
VJ
1762 let slice: &[u8] = unsafe {
1763 std::slice::from_raw_parts(input as *mut u8, len as usize)
1764 };
1765 match parse_nbss_record_partial(slice) {
1766 IResult::Done(_, ref hdr) => {
1767 if hdr.is_smb() {
1768 return 1;
1769 }
1770 },
1771 _ => { },
1772 }
75d7c9d6
VJ
1773 return 1
1774}
1775
1776#[no_mangle]
1777pub extern "C" fn rs_smb_state_get_tx_count(state: &mut SMBState)
1778 -> libc::uint64_t
1779{
1780 SCLogDebug!("rs_smb_state_get_tx_count: returning {}", state.tx_id);
1781 return state.tx_id;
1782}
1783
1784#[no_mangle]
1785pub extern "C" fn rs_smb_state_get_tx(state: &mut SMBState,
1786 tx_id: libc::uint64_t)
1787 -> *mut SMBTransaction
1788{
1789 match state.get_tx_by_id(tx_id) {
1790 Some(tx) => {
1791 return unsafe{transmute(tx)};
1792 }
1793 None => {
1794 return std::ptr::null_mut();
1795 }
1796 }
1797}
1798
1799// for use with the C API call StateGetTxIterator
1800#[no_mangle]
1801pub extern "C" fn rs_smb_state_get_tx_iterator(
1802 state: &mut SMBState,
1803 min_tx_id: libc::uint64_t,
1804 istate: &mut libc::uint64_t)
1805 -> applayer::AppLayerGetTxIterTuple
1806{
1807 match state.get_tx_iterator(min_tx_id, istate) {
1808 Some((tx, out_tx_id, has_next)) => {
1809 let c_tx = unsafe { transmute(tx) };
1810 let ires = applayer::AppLayerGetTxIterTuple::with_values(c_tx, out_tx_id, has_next);
1811 return ires;
1812 }
1813 None => {
1814 return applayer::AppLayerGetTxIterTuple::not_found();
1815 }
1816 }
1817}
1818
1819#[no_mangle]
1820pub extern "C" fn rs_smb_state_tx_free(state: &mut SMBState,
1821 tx_id: libc::uint64_t)
1822{
1823 SCLogDebug!("freeing tx {}", tx_id as u64);
1824 state.free_tx(tx_id);
1825}
1826
1827#[no_mangle]
1828pub extern "C" fn rs_smb_state_progress_completion_status(
1829 _direction: libc::uint8_t)
1830 -> libc::c_int
1831{
1832 return 1;
1833}
1834
1835#[no_mangle]
1836pub extern "C" fn rs_smb_tx_get_alstate_progress(tx: &mut SMBTransaction,
1837 direction: libc::uint8_t)
1838 -> libc::uint8_t
1839{
1840 if direction == STREAM_TOSERVER && tx.request_done {
1841 SCLogDebug!("tx {} TOSERVER progress 1 => {:?}", tx.id, tx);
1842 return 1;
1843 } else if direction == STREAM_TOCLIENT && tx.response_done {
1844 SCLogDebug!("tx {} TOCLIENT progress 1 => {:?}", tx.id, tx);
1845 return 1;
1846 } else {
1847 SCLogDebug!("tx {} direction {} progress 0", tx.id, direction);
1848 return 0;
1849 }
1850}
1851
1852#[no_mangle]
1853pub extern "C" fn rs_smb_tx_set_logged(_state: &mut SMBState,
1854 tx: &mut SMBTransaction,
1855 bits: libc::uint32_t)
1856{
1857 tx.logged.set(bits);
1858}
1859
1860#[no_mangle]
1861pub extern "C" fn rs_smb_tx_get_logged(_state: &mut SMBState,
1862 tx: &mut SMBTransaction)
1863 -> u32
1864{
1865 return tx.logged.get();
1866}
1867
1868#[no_mangle]
1869pub extern "C" fn rs_smb_tx_set_detect_flags(
1870 tx: &mut SMBTransaction,
1871 direction: libc::uint8_t,
1872 flags: libc::uint64_t)
1873{
1874 if (direction & STREAM_TOSERVER) != 0 {
1875 tx.detect_flags_ts = flags as u64;
1876 } else {
1877 tx.detect_flags_tc = flags as u64;
1878 }
1879}
1880
1881#[no_mangle]
1882pub extern "C" fn rs_smb_tx_get_detect_flags(
1883 tx: &mut SMBTransaction,
1884 direction: libc::uint8_t)
1885 -> libc::uint64_t
1886{
1887 if (direction & STREAM_TOSERVER) != 0 {
1888 return tx.detect_flags_ts as libc::uint64_t;
1889 } else {
1890 return tx.detect_flags_tc as libc::uint64_t;
1891 }
1892}
1893
1894#[no_mangle]
1895pub extern "C" fn rs_smb_state_set_tx_detect_state(
1896 tx: &mut SMBTransaction,
1897 de_state: &mut DetectEngineState)
1898{
1899 tx.de_state = Some(de_state);
1900}
1901
1902#[no_mangle]
1903pub extern "C" fn rs_smb_state_get_tx_detect_state(
1904 tx: &mut SMBTransaction)
1905 -> *mut DetectEngineState
1906{
1907 match tx.de_state {
1908 Some(ds) => {
1909 return ds;
1910 },
1911 None => {
1912 return std::ptr::null_mut();
1913 }
1914 }
1915}
1916
1917#[no_mangle]
1918pub extern "C" fn rs_smb_state_truncate(
1919 state: &mut SMBState,
1920 direction: libc::uint8_t)
1921{
1922 if (direction & STREAM_TOSERVER) != 0 {
1923 state.trunc_ts();
1924 } else {
1925 state.trunc_tc();
1926 }
1927}
1928
1929#[no_mangle]
1930pub extern "C" fn rs_smb_state_get_events(state: &mut SMBState,
1931 tx_id: libc::uint64_t)
1932 -> *mut AppLayerDecoderEvents
1933{
1934 match state.get_tx_by_id(tx_id) {
1935 Some(tx) => {
1936 return tx.events;
1937 }
1938 _ => {
1939 return std::ptr::null_mut();
1940 }
1941 }
1942}
1943
1944#[no_mangle]
1945pub extern "C" fn rs_smb_state_get_event_info(event_name: *const libc::c_char,
1946 event_id: *mut libc::c_int,
1947 event_type: *mut AppLayerEventType)
1948 -> i8
1949{
1950 if event_name == std::ptr::null() {
1951 return -1;
1952 }
1953 let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) };
1954 let event = match c_event_name.to_str() {
1955 Ok(s) => {
1956 smb_str_to_event(s)
1957 },
1958 Err(_) => -1, // UTF-8 conversion failed
1959 };
1960 unsafe {
1961 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
1962 *event_id = event as libc::c_int;
1963 };
1964 if event == -1 {
1965 return -1;
1966 }
1967 0
1968}