1 /* Copyright (C) 2018 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 use nom;
23
24 use crate::core::*;
25
26 use crate::smb::smb::*;
27 use crate::smb::dcerpc::*;
28 use crate::smb::events::*;
29 use crate::smb::files::*;
30
31 use crate::smb::smb1_records::*;
32 use crate::smb::smb1_session::*;
33
34 // https://msdn.microsoft.com/en-us/library/ee441741.aspx
35 pub const SMB1_COMMAND_CREATE_DIRECTORY: u8 = 0x00;
36 pub const SMB1_COMMAND_DELETE_DIRECTORY: u8 = 0x01;
37 pub const SMB1_COMMAND_OPEN: u8 = 0x02;
38 pub const SMB1_COMMAND_CREATE: u8 = 0x03;
39 pub const SMB1_COMMAND_CLOSE: u8 = 0x04;
40 pub const SMB1_COMMAND_FLUSH: u8 = 0x05;
41 pub const SMB1_COMMAND_DELETE: u8 = 0x06;
42 pub const SMB1_COMMAND_RENAME: u8 = 0x07;
43 pub const SMB1_COMMAND_QUERY_INFORMATION: u8 = 0x08;
44 pub const SMB1_COMMAND_SET_INFORMATION: u8 = 0x09;
45 pub const SMB1_COMMAND_READ: u8 = 0x0a;
46 pub const SMB1_COMMAND_WRITE: u8 = 0x0b;
47 pub const SMB1_COMMAND_LOCK_BYTE_RANGE: u8 = 0x0c;
48 pub const SMB1_COMMAND_UNLOCK_BYTE_RANGE: u8 = 0x0d;
49 pub const SMB1_COMMAND_CREATE_TEMPORARY: u8 = 0x0e;
50 pub const SMB1_COMMAND_CREATE_NEW: u8 = 0x0f;
51 pub const SMB1_COMMAND_CHECK_DIRECTORY: u8 = 0x10;
52 pub const SMB1_COMMAND_PROCESS_EXIT: u8 = 0x11;
53 pub const SMB1_COMMAND_SEEK: u8 = 0x12;
54 pub const SMB1_COMMAND_LOCK_AND_READ: u8 = 0x13;
55 pub const SMB1_COMMAND_WRITE_AND_UNLOCK: u8 = 0x14;
56 pub const SMB1_COMMAND_LOCKING_ANDX: u8 = 0x24;
57 pub const SMB1_COMMAND_TRANS: u8 = 0x25;
58 pub const SMB1_COMMAND_ECHO: u8 = 0x2b;
59 pub const SMB1_COMMAND_WRITE_AND_CLOSE: u8 = 0x2c;
60 pub const SMB1_COMMAND_OPEN_ANDX: u8 = 0x2d;
61 pub const SMB1_COMMAND_READ_ANDX: u8 = 0x2e;
62 pub const SMB1_COMMAND_WRITE_ANDX: u8 = 0x2f;
63 pub const SMB1_COMMAND_TRANS2: u8 = 0x32;
64 pub const SMB1_COMMAND_TRANS2_SECONDARY: u8 = 0x33;
65 pub const SMB1_COMMAND_FIND_CLOSE2: u8 = 0x34;
66 pub const SMB1_COMMAND_TREE_DISCONNECT: u8 = 0x71;
67 pub const SMB1_COMMAND_NEGOTIATE_PROTOCOL: u8 = 0x72;
68 pub const SMB1_COMMAND_SESSION_SETUP_ANDX: u8 = 0x73;
69 pub const SMB1_COMMAND_LOGOFF_ANDX: u8 = 0x74;
70 pub const SMB1_COMMAND_TREE_CONNECT_ANDX: u8 = 0x75;
71 pub const SMB1_COMMAND_QUERY_INFO_DISK: u8 = 0x80;
72 pub const SMB1_COMMAND_NT_TRANS: u8 = 0xa0;
73 pub const SMB1_COMMAND_NT_CREATE_ANDX: u8 = 0xa2;
74 pub const SMB1_COMMAND_NT_CANCEL: u8 = 0xa4;
75
smb1_command_string(c: u8) -> String76 pub fn smb1_command_string(c: u8) -> String {
77 match c {
78 SMB1_COMMAND_CREATE_DIRECTORY => "SMB1_COMMAND_CREATE_DIRECTORY",
79 SMB1_COMMAND_DELETE_DIRECTORY => "SMB1_COMMAND_DELETE_DIRECTORY",
80 SMB1_COMMAND_OPEN => "SMB1_COMMAND_OPEN",
81 SMB1_COMMAND_CREATE => "SMB1_COMMAND_CREATE",
82 SMB1_COMMAND_CLOSE => "SMB1_COMMAND_CLOSE",
83 SMB1_COMMAND_FLUSH => "SMB1_COMMAND_FLUSH",
84 SMB1_COMMAND_DELETE => "SMB1_COMMAND_DELETE",
85 SMB1_COMMAND_RENAME => "SMB1_COMMAND_RENAME",
86 SMB1_COMMAND_QUERY_INFORMATION => "SMB1_COMMAND_QUERY_INFORMATION",
87 SMB1_COMMAND_SET_INFORMATION => "SMB1_COMMAND_SET_INFORMATION",
88 SMB1_COMMAND_READ => "SMB1_COMMAND_READ",
89 SMB1_COMMAND_WRITE => "SMB1_COMMAND_WRITE",
90 SMB1_COMMAND_LOCK_BYTE_RANGE => "SMB1_COMMAND_LOCK_BYTE_RANGE",
91 SMB1_COMMAND_UNLOCK_BYTE_RANGE => "SMB1_COMMAND_UNLOCK_BYTE_RANGE",
92 SMB1_COMMAND_CREATE_TEMPORARY => "SMB1_COMMAND_CREATE_TEMPORARY",
93 SMB1_COMMAND_CREATE_NEW => "SMB1_COMMAND_CREATE_NEW",
94 SMB1_COMMAND_CHECK_DIRECTORY => "SMB1_COMMAND_CHECK_DIRECTORY",
95 SMB1_COMMAND_PROCESS_EXIT => "SMB1_COMMAND_PROCESS_EXIT",
96 SMB1_COMMAND_SEEK => "SMB1_COMMAND_SEEK",
97 SMB1_COMMAND_LOCK_AND_READ => "SMB1_COMMAND_LOCK_AND_READ",
98 SMB1_COMMAND_WRITE_AND_UNLOCK => "SMB1_COMMAND_WRITE_AND_UNLOCK",
99 SMB1_COMMAND_LOCKING_ANDX => "SMB1_COMMAND_LOCKING_ANDX",
100 SMB1_COMMAND_ECHO => "SMB1_COMMAND_ECHO",
101 SMB1_COMMAND_WRITE_AND_CLOSE => "SMB1_COMMAND_WRITE_AND_CLOSE",
102 SMB1_COMMAND_OPEN_ANDX => "SMB1_COMMAND_OPEN_ANDX",
103 SMB1_COMMAND_READ_ANDX => "SMB1_COMMAND_READ_ANDX",
104 SMB1_COMMAND_WRITE_ANDX => "SMB1_COMMAND_WRITE_ANDX",
105 SMB1_COMMAND_TRANS => "SMB1_COMMAND_TRANS",
106 SMB1_COMMAND_TRANS2 => "SMB1_COMMAND_TRANS2",
107 SMB1_COMMAND_TRANS2_SECONDARY => "SMB1_COMMAND_TRANS2_SECONDARY",
108 SMB1_COMMAND_FIND_CLOSE2 => "SMB1_COMMAND_FIND_CLOSE2",
109 SMB1_COMMAND_TREE_DISCONNECT => "SMB1_COMMAND_TREE_DISCONNECT",
110 SMB1_COMMAND_NEGOTIATE_PROTOCOL => "SMB1_COMMAND_NEGOTIATE_PROTOCOL",
111 SMB1_COMMAND_SESSION_SETUP_ANDX => "SMB1_COMMAND_SESSION_SETUP_ANDX",
112 SMB1_COMMAND_LOGOFF_ANDX => "SMB1_COMMAND_LOGOFF_ANDX",
113 SMB1_COMMAND_TREE_CONNECT_ANDX => "SMB1_COMMAND_TREE_CONNECT_ANDX",
114 SMB1_COMMAND_QUERY_INFO_DISK => "SMB1_COMMAND_QUERY_INFO_DISK",
115 SMB1_COMMAND_NT_TRANS => "SMB1_COMMAND_NT_TRANS",
116 SMB1_COMMAND_NT_CREATE_ANDX => "SMB1_COMMAND_NT_CREATE_ANDX",
117 SMB1_COMMAND_NT_CANCEL => "SMB1_COMMAND_NT_CANCEL",
118 _ => { return (c).to_string(); },
119 }.to_string()
120 }
121
122 // later we'll use this to determine if we need to
123 // track a ssn per type
smb1_create_new_tx(cmd: u8) -> bool124 pub fn smb1_create_new_tx(cmd: u8) -> bool {
125 match cmd {
126 SMB1_COMMAND_READ_ANDX |
127 SMB1_COMMAND_WRITE_ANDX |
128 SMB1_COMMAND_TRANS |
129 SMB1_COMMAND_TRANS2 => { false },
130 _ => { true },
131 }
132 }
133
134 // see if we're going to do a lookup for a TX.
135 // related to smb1_create_new_tx(), however it
136 // excludes the 'maybe' commands like TRANS2
smb1_check_tx(cmd: u8) -> bool137 pub fn smb1_check_tx(cmd: u8) -> bool {
138 match cmd {
139 SMB1_COMMAND_READ_ANDX |
140 SMB1_COMMAND_WRITE_ANDX |
141 SMB1_COMMAND_TRANS => { false },
142 _ => { true },
143 }
144 }
145
smb1_close_file(state: &mut SMBState, fid: &Vec<u8>)146 fn smb1_close_file(state: &mut SMBState, fid: &Vec<u8>)
147 {
148 // we can have created 2 txs for a FID: one for reads
149 // and one for writes. So close both.
150 match state.get_file_tx_by_fuid(&fid, STREAM_TOSERVER) {
151 Some((tx, files, flags)) => {
152 SCLogDebug!("found tx {}", tx.id);
153 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
154 if !tx.request_done {
155 SCLogDebug!("closing file tx {} FID {:?}", tx.id, fid);
156 tdf.file_tracker.close(files, flags);
157 tx.request_done = true;
158 tx.response_done = true;
159 SCLogDebug!("tx {} is done", tx.id);
160 }
161 }
162 },
163 None => { },
164 }
165 match state.get_file_tx_by_fuid(&fid, STREAM_TOCLIENT) {
166 Some((tx, files, flags)) => {
167 SCLogDebug!("found tx {}", tx.id);
168 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
169 if !tx.request_done {
170 SCLogDebug!("closing file tx {} FID {:?}", tx.id, fid);
171 tdf.file_tracker.close(files, flags);
172 tx.request_done = true;
173 tx.response_done = true;
174 SCLogDebug!("tx {} is done", tx.id);
175 }
176 }
177 },
178 None => { },
179 }
180 }
181
smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32182 pub fn smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 {
183 SCLogDebug!("record: command {}: record {:?}", r.command, r);
184
185 let mut events : Vec<SMBEvent> = Vec::new();
186 let mut no_response_expected = false;
187
188 let have_tx = match r.command {
189 SMB1_COMMAND_RENAME => {
190 match parse_smb_rename_request_record(r.data) {
191 Ok((_, rd)) => {
192 SCLogDebug!("RENAME {:?}", rd);
193
194 let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
195 let mut newname = rd.newname;
196 newname.retain(|&i|i != 0x00);
197 let mut oldname = rd.oldname;
198 oldname.retain(|&i|i != 0x00);
199
200 let tx = state.new_rename_tx(Vec::new(), oldname, newname);
201 tx.hdr = tx_hdr;
202 tx.request_done = true;
203 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_RENAME);
204 true
205 },
206 _ => {
207 events.push(SMBEvent::MalformedData);
208 false
209 },
210 }
211 },
212 SMB1_COMMAND_TRANS2 => {
213 match parse_smb_trans2_request_record(r.data) {
214 Ok((_, rd)) => {
215 SCLogDebug!("TRANS2 DONE {:?}", rd);
216
217 if rd.subcmd == 6 {
218 SCLogDebug!("SET_PATH_INFO");
219 match parse_trans2_request_params_set_path_info(rd.setup_blob) {
220 Ok((_, pd)) => {
221 SCLogDebug!("TRANS2 SET_PATH_INFO PARAMS DONE {:?}", pd);
222
223 if pd.loi == 1013 { // set disposition info
224 match parse_trans2_request_data_set_file_info_disposition(rd.data_blob) {
225 Ok((_, disp)) => {
226 SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION DONE {:?}", disp);
227 let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
228
229 let tx = state.new_setpathinfo_tx(pd.oldname,
230 rd.subcmd, pd.loi, disp.delete);
231 tx.hdr = tx_hdr;
232 tx.request_done = true;
233 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
234 true
235
236 },
237 Err(nom::Err::Incomplete(_n)) => {
238 SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION INCOMPLETE {:?}", _n);
239 events.push(SMBEvent::MalformedData);
240 false
241 },
242 Err(nom::Err::Error(_e)) |
243 Err(nom::Err::Failure(_e)) => {
244 SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION ERROR {:?}", _e);
245 events.push(SMBEvent::MalformedData);
246 false
247 },
248 }
249 } else if pd.loi == 1010 {
250 match parse_trans2_request_data_set_path_info_rename(rd.data_blob) {
251 Ok((_, ren)) => {
252 SCLogDebug!("TRANS2 SET_PATH_INFO DATA RENAME DONE {:?}", ren);
253 let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
254 let mut newname = ren.newname.to_vec();
255 newname.retain(|&i|i != 0x00);
256
257 let fid : Vec<u8> = Vec::new();
258
259 let tx = state.new_rename_tx(fid, pd.oldname, newname);
260 tx.hdr = tx_hdr;
261 tx.request_done = true;
262 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
263 true
264 },
265 Err(nom::Err::Incomplete(_n)) => {
266 SCLogDebug!("TRANS2 SET_PATH_INFO DATA RENAME INCOMPLETE {:?}", _n);
267 events.push(SMBEvent::MalformedData);
268 false
269 },
270 Err(nom::Err::Error(_e)) |
271 Err(nom::Err::Failure(_e)) => {
272 SCLogDebug!("TRANS2 SET_PATH_INFO DATA RENAME ERROR {:?}", _e);
273 events.push(SMBEvent::MalformedData);
274 false
275 },
276 }
277 } else {
278 false
279 }
280 },
281 Err(nom::Err::Incomplete(_n)) => {
282 SCLogDebug!("TRANS2 SET_PATH_INFO PARAMS INCOMPLETE {:?}", _n);
283 events.push(SMBEvent::MalformedData);
284 false
285 },
286 Err(nom::Err::Error(_e)) |
287 Err(nom::Err::Failure(_e)) => {
288 SCLogDebug!("TRANS2 SET_PATH_INFO PARAMS ERROR {:?}", _e);
289 events.push(SMBEvent::MalformedData);
290 false
291 },
292 }
293 } else if rd.subcmd == 8 {
294 SCLogDebug!("SET_FILE_INFO");
295 match parse_trans2_request_params_set_file_info(rd.setup_blob) {
296 Ok((_, pd)) => {
297 SCLogDebug!("TRANS2 SET_FILE_INFO PARAMS DONE {:?}", pd);
298
299 if pd.loi == 1013 { // set disposition info
300 match parse_trans2_request_data_set_file_info_disposition(rd.data_blob) {
301 Ok((_, disp)) => {
302 SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION DONE {:?}", disp);
303 let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
304
305 let mut frankenfid = pd.fid.to_vec();
306 frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id));
307
308 let filename = match state.guid2name_map.get(&frankenfid) {
309 Some(n) => n.to_vec(),
310 None => b"<unknown>".to_vec(),
311 };
312 let tx = state.new_setfileinfo_tx(filename, pd.fid.to_vec(),
313 rd.subcmd, pd.loi, disp.delete);
314 tx.hdr = tx_hdr;
315 tx.request_done = true;
316 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
317 true
318
319 },
320 Err(nom::Err::Incomplete(_n)) => {
321 SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION INCOMPLETE {:?}", _n);
322 events.push(SMBEvent::MalformedData);
323 false
324 },
325 Err(nom::Err::Error(_e)) |
326 Err(nom::Err::Failure(_e)) => {
327 SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION ERROR {:?}", _e);
328 events.push(SMBEvent::MalformedData);
329 false
330 },
331 }
332 } else if pd.loi == 1010 {
333 match parse_trans2_request_data_set_file_info_rename(rd.data_blob) {
334 Ok((_, ren)) => {
335 SCLogDebug!("TRANS2 SET_FILE_INFO DATA RENAME DONE {:?}", ren);
336 let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
337 let mut newname = ren.newname.to_vec();
338 newname.retain(|&i|i != 0x00);
339
340 let mut frankenfid = pd.fid.to_vec();
341 frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id));
342
343 let oldname = match state.guid2name_map.get(&frankenfid) {
344 Some(n) => n.to_vec(),
345 None => b"<unknown>".to_vec(),
346 };
347 let tx = state.new_rename_tx(pd.fid.to_vec(), oldname, newname);
348 tx.hdr = tx_hdr;
349 tx.request_done = true;
350 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
351 true
352 },
353 Err(nom::Err::Incomplete(_n)) => {
354 SCLogDebug!("TRANS2 SET_FILE_INFO DATA RENAME INCOMPLETE {:?}", _n);
355 events.push(SMBEvent::MalformedData);
356 false
357 },
358 Err(nom::Err::Error(_e)) |
359 Err(nom::Err::Failure(_e)) => {
360 SCLogDebug!("TRANS2 SET_FILE_INFO DATA RENAME ERROR {:?}", _e);
361 events.push(SMBEvent::MalformedData);
362 false
363 },
364 }
365 } else {
366 false
367 }
368 },
369 Err(nom::Err::Incomplete(_n)) => {
370 SCLogDebug!("TRANS2 SET_FILE_INFO PARAMS INCOMPLETE {:?}", _n);
371 events.push(SMBEvent::MalformedData);
372 false
373 },
374 Err(nom::Err::Error(_e)) |
375 Err(nom::Err::Failure(_e)) => {
376 SCLogDebug!("TRANS2 SET_FILE_INFO PARAMS ERROR {:?}", _e);
377 events.push(SMBEvent::MalformedData);
378 false
379 },
380 }
381 } else {
382 false
383 }
384 },
385 Err(nom::Err::Incomplete(_n)) => {
386 SCLogDebug!("TRANS2 INCOMPLETE {:?}", _n);
387 events.push(SMBEvent::MalformedData);
388 false
389 },
390 Err(nom::Err::Error(_e)) |
391 Err(nom::Err::Failure(_e)) => {
392 SCLogDebug!("TRANS2 ERROR {:?}", _e);
393 events.push(SMBEvent::MalformedData);
394 false
395 },
396 }
397 },
398 SMB1_COMMAND_READ_ANDX => {
399 match parse_smb_read_andx_request_record(r.data) {
400 Ok((_, rr)) => {
401 SCLogDebug!("rr {:?}", rr);
402
403 // store read fid,offset in map
404 let fid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_OFFSET);
405 let mut fid = rr.fid.to_vec();
406 fid.extend_from_slice(&u32_as_bytes(r.ssn_id));
407 let fidoff = SMBFileGUIDOffset::new(fid, rr.offset);
408 state.ssn2vecoffset_map.insert(fid_key, fidoff);
409 },
410 _ => {
411 events.push(SMBEvent::MalformedData);
412 },
413 }
414 false
415 },
416 SMB1_COMMAND_WRITE_ANDX |
417 SMB1_COMMAND_WRITE |
418 SMB1_COMMAND_WRITE_AND_CLOSE => {
419 smb1_write_request_record(state, r);
420 true // tx handling in func
421 },
422 SMB1_COMMAND_TRANS => {
423 smb1_trans_request_record(state, r);
424 true
425 },
426 SMB1_COMMAND_NEGOTIATE_PROTOCOL => {
427 match parse_smb1_negotiate_protocol_record(r.data) {
428 Ok((_, pr)) => {
429 SCLogDebug!("SMB_COMMAND_NEGOTIATE_PROTOCOL {:?}", pr);
430
431 let mut bad_dialects = false;
432 let mut dialects : Vec<Vec<u8>> = Vec::new();
433 for d in &pr.dialects {
434 if d.len() == 0 {
435 bad_dialects = true;
436 continue;
437 } else if d.len() == 1 {
438 bad_dialects = true;
439 }
440 let x = &d[1..d.len()];
441 let dvec = x.to_vec();
442 dialects.push(dvec);
443 }
444
445 let found = match state.get_negotiate_tx(1) {
446 Some(tx) => {
447 SCLogDebug!("WEIRD, should not have NEGOTIATE tx!");
448 tx.set_event(SMBEvent::DuplicateNegotiate);
449 true
450 },
451 None => { false },
452 };
453 if !found {
454 let tx = state.new_negotiate_tx(1);
455 if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
456 tdn.dialects = dialects;
457 }
458 tx.request_done = true;
459 if bad_dialects {
460 tx.set_event(SMBEvent::NegotiateMalformedDialects);
461 }
462 }
463 true
464 },
465 _ => {
466 events.push(SMBEvent::MalformedData);
467 false
468 },
469 }
470 },
471 SMB1_COMMAND_NT_CREATE_ANDX => {
472 match parse_smb_create_andx_request_record(r.data, r) {
473 Ok((_, cr)) => {
474 SCLogDebug!("Create AndX {:?}", cr);
475 let del = cr.create_options & 0x0000_1000 != 0;
476 let dir = cr.create_options & 0x0000_0001 != 0;
477 SCLogDebug!("del {} dir {} options {:08x}", del, dir, cr.create_options);
478
479 let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_FILENAME);
480 let name_val = cr.file_name.to_vec();
481 state.ssn2vec_map.insert(name_key, name_val);
482
483 let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
484 let tx = state.new_create_tx(&cr.file_name.to_vec(),
485 cr.disposition, del, dir, tx_hdr);
486 tx.vercmd.set_smb1_cmd(r.command);
487 SCLogDebug!("TS CREATE TX {} created", tx.id);
488 true
489 },
490 _ => {
491 events.push(SMBEvent::MalformedData);
492 false
493 },
494 }
495 },
496 SMB1_COMMAND_SESSION_SETUP_ANDX => {
497 SCLogDebug!("SMB1_COMMAND_SESSION_SETUP_ANDX user_id {}", r.user_id);
498 smb1_session_setup_request(state, r);
499 true
500 },
501 SMB1_COMMAND_TREE_CONNECT_ANDX => {
502 SCLogDebug!("SMB1_COMMAND_TREE_CONNECT_ANDX");
503 match parse_smb_connect_tree_andx_record(r.data, r) {
504 Ok((_, tr)) => {
505 let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_TREE);
506 let mut name_val = tr.path;
507 if name_val.len() > 1 {
508 name_val = name_val[1..].to_vec();
509 }
510
511 // store hdr as SMBHDR_TYPE_TREE, so with tree id 0
512 // when the response finds this we update it
513 let tx = state.new_treeconnect_tx(name_key, name_val);
514 if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
515 tdn.req_service = Some(tr.service.to_vec());
516 }
517 tx.request_done = true;
518 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TREE_CONNECT_ANDX);
519 true
520 },
521 _ => {
522 events.push(SMBEvent::MalformedData);
523 false
524 },
525 }
526 },
527 SMB1_COMMAND_TREE_DISCONNECT => {
528 let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
529 state.ssn2tree_map.remove(&tree_key);
530 false
531 },
532 SMB1_COMMAND_CLOSE => {
533 match parse_smb1_close_request_record(r.data) {
534 Ok((_, cd)) => {
535 let mut fid = cd.fid.to_vec();
536 fid.extend_from_slice(&u32_as_bytes(r.ssn_id));
537 SCLogDebug!("closing FID {:?}/{:?}", cd.fid, fid);
538 smb1_close_file(state, &fid);
539 },
540 _ => {
541 events.push(SMBEvent::MalformedData);
542 },
543 }
544 false
545 },
546 SMB1_COMMAND_NT_CANCEL |
547 SMB1_COMMAND_TRANS2_SECONDARY |
548 SMB1_COMMAND_LOCKING_ANDX => {
549 no_response_expected = true;
550 false
551 },
552 _ => {
553 if r.command == SMB1_COMMAND_LOGOFF_ANDX ||
554 r.command == SMB1_COMMAND_TREE_DISCONNECT ||
555 r.command == SMB1_COMMAND_NT_TRANS ||
556 r.command == SMB1_COMMAND_NT_CANCEL ||
557 r.command == SMB1_COMMAND_RENAME ||
558 r.command == SMB1_COMMAND_CHECK_DIRECTORY ||
559 r.command == SMB1_COMMAND_ECHO ||
560 r.command == SMB1_COMMAND_TRANS
561 { } else {
562 SCLogDebug!("unsupported command {}/{}",
563 r.command, &smb1_command_string(r.command));
564 }
565 false
566 },
567 };
568 if !have_tx {
569 if smb1_create_new_tx(r.command) {
570 let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
571 let tx = state.new_generic_tx(1, r.command as u16, tx_key);
572 SCLogDebug!("tx {} created for {}/{}", tx.id, r.command, &smb1_command_string(r.command));
573 tx.set_events(events);
574 if no_response_expected {
575 tx.response_done = true;
576 }
577 }
578 }
579 0
580 }
581
smb1_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32582 pub fn smb1_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 {
583 SCLogDebug!("record: command {} status {} -> {:?}", r.command, r.nt_status, r);
584
585 let key_ssn_id = r.ssn_id;
586 let key_tree_id = r.tree_id;
587 let key_multiplex_id = r.multiplex_id;
588 let mut tx_sync = false;
589 let mut events : Vec<SMBEvent> = Vec::new();
590
591 let have_tx = match r.command {
592 SMB1_COMMAND_READ_ANDX => {
593 smb1_read_response_record(state, &r);
594 true // tx handling in func
595 },
596 SMB1_COMMAND_NEGOTIATE_PROTOCOL => {
597 SCLogDebug!("SMB1_COMMAND_NEGOTIATE_PROTOCOL response");
598 match parse_smb1_negotiate_protocol_response_record(r.data) {
599 Ok((_, pr)) => {
600 let (have_ntx, dialect) = match state.get_negotiate_tx(1) {
601 Some(tx) => {
602 tx.set_status(r.nt_status, r.is_dos_error);
603 tx.response_done = true;
604 SCLogDebug!("tx {} is done", tx.id);
605 let d = match tx.type_data {
606 Some(SMBTransactionTypeData::NEGOTIATE(ref mut x)) => {
607 x.server_guid = pr.server_guid.to_vec();
608
609 let dialect_idx = pr.dialect_idx as usize;
610 if x.dialects.len() <= dialect_idx {
611 None
612 } else {
613 let d = x.dialects[dialect_idx].to_vec();
614 Some(d)
615 }
616 },
617 _ => { None },
618 };
619 if d == None {
620 tx.set_event(SMBEvent::NegotiateMalformedDialects);
621 }
622 (true, d)
623 },
624 None => { (false, None) },
625 };
626 if let Some(d) = dialect {
627 SCLogDebug!("dialect {:?}", d);
628 state.dialect_vec = Some(d);
629 }
630 have_ntx
631 },
632 _ => {
633 events.push(SMBEvent::MalformedData);
634 false
635 },
636 }
637 },
638 SMB1_COMMAND_TREE_CONNECT_ANDX => {
639 if r.nt_status != SMB_NTSTATUS_SUCCESS {
640 let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_TREE);
641 match state.get_treeconnect_tx(name_key) {
642 Some(tx) => {
643 if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
644 tdn.tree_id = r.tree_id as u32;
645 }
646 tx.set_status(r.nt_status, r.is_dos_error);
647 tx.response_done = true;
648 SCLogDebug!("tx {} is done", tx.id);
649 },
650 None => { },
651 }
652 return 0;
653 }
654
655 match parse_smb_connect_tree_andx_response_record(r.data) {
656 Ok((_, tr)) => {
657 let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_TREE);
658 let is_pipe = tr.service == "IPC".as_bytes();
659 let mut share_name = Vec::new();
660 let found = match state.get_treeconnect_tx(name_key) {
661 Some(tx) => {
662 if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
663 tdn.is_pipe = is_pipe;
664 tdn.tree_id = r.tree_id as u32;
665 share_name = tdn.share_name.to_vec();
666 tdn.res_service = Some(tr.service.to_vec());
667 }
668 tx.hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
669 tx.set_status(r.nt_status, r.is_dos_error);
670 tx.response_done = true;
671 SCLogDebug!("tx {} is done", tx.id);
672 true
673 },
674 None => { false },
675 };
676 if found {
677 let tree = SMBTree::new(share_name.to_vec(), is_pipe);
678 let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
679 state.ssn2tree_map.insert(tree_key, tree);
680 }
681 found
682 },
683 _ => {
684 events.push(SMBEvent::MalformedData);
685 false
686 },
687 }
688 },
689 SMB1_COMMAND_TREE_DISCONNECT => {
690 // normally removed when processing request,
691 // but in case we missed that try again here
692 let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
693 state.ssn2tree_map.remove(&tree_key);
694 false
695 },
696 SMB1_COMMAND_NT_CREATE_ANDX => {
697 SCLogDebug!("SMB1_COMMAND_NT_CREATE_ANDX response {:08x}", r.nt_status);
698 if r.nt_status == SMB_NTSTATUS_SUCCESS {
699 match parse_smb_create_andx_response_record(r.data) {
700 Ok((_, cr)) => {
701 SCLogDebug!("Create AndX {:?}", cr);
702
703 let guid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_FILENAME);
704 match state.ssn2vec_map.remove(&guid_key) {
705 Some(mut p) => {
706 p.retain(|&i|i != 0x00);
707
708 let mut fid = cr.fid.to_vec();
709 fid.extend_from_slice(&u32_as_bytes(r.ssn_id));
710 SCLogDebug!("SMB1_COMMAND_NT_CREATE_ANDX fid {:?}", fid);
711 SCLogDebug!("fid {:?} name {:?}", fid, p);
712 state.guid2name_map.insert(fid, p);
713 },
714 _ => {
715 SCLogDebug!("SMBv1 response: GUID NOT FOUND");
716 },
717 }
718
719 let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
720 if let Some(tx) = state.get_generic_tx(1, r.command as u16, &tx_hdr) {
721 SCLogDebug!("tx {} with {}/{} marked as done",
722 tx.id, r.command, &smb1_command_string(r.command));
723 tx.set_status(r.nt_status, false);
724 tx.response_done = true;
725
726 if let Some(SMBTransactionTypeData::CREATE(ref mut tdn)) = tx.type_data {
727 tdn.create_ts = cr.create_ts.as_unix();
728 tdn.last_access_ts = cr.last_access_ts.as_unix();
729 tdn.last_write_ts = cr.last_write_ts.as_unix();
730 tdn.last_change_ts = cr.last_change_ts.as_unix();
731 tdn.size = cr.file_size;
732 tdn.guid = cr.fid.to_vec();
733 }
734 }
735 true
736 },
737 _ => {
738 events.push(SMBEvent::MalformedData);
739 false
740 },
741 }
742 } else {
743 false
744 }
745 },
746 SMB1_COMMAND_TRANS => {
747 smb1_trans_response_record(state, r);
748 true
749 },
750 SMB1_COMMAND_SESSION_SETUP_ANDX => {
751 smb1_session_setup_response(state, r);
752 true
753 },
754 SMB1_COMMAND_LOGOFF_ANDX => {
755 tx_sync = true;
756 false
757 },
758 _ => {
759 false
760 },
761 };
762
763 if !have_tx && tx_sync {
764 match state.get_last_tx(1, r.command as u16) {
765 Some(tx) => {
766 SCLogDebug!("last TX {} is CMD {}", tx.id, &smb1_command_string(r.command));
767 tx.response_done = true;
768 SCLogDebug!("tx {} cmd {} is done", tx.id, r.command);
769 tx.set_status(r.nt_status, r.is_dos_error);
770 tx.set_events(events);
771 },
772 _ => {},
773 }
774 } else if !have_tx && smb1_check_tx(r.command) {
775 let tx_key = SMBCommonHdr::new(SMBHDR_TYPE_GENERICTX,
776 key_ssn_id as u64, key_tree_id as u32, key_multiplex_id as u64);
777 let _have_tx2 = match state.get_generic_tx(1, r.command as u16, &tx_key) {
778 Some(tx) => {
779 tx.request_done = true;
780 tx.response_done = true;
781 SCLogDebug!("tx {} cmd {} is done", tx.id, r.command);
782 tx.set_status(r.nt_status, r.is_dos_error);
783 tx.set_events(events);
784 true
785 },
786 _ => {
787 SCLogDebug!("no TX found for key {:?}", tx_key);
788 false
789 },
790 };
791 } else {
792 SCLogDebug!("have tx for cmd {}", r.command);
793 }
794 0
795 }
796
smb1_trans_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)797 pub fn smb1_trans_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
798 {
799 let mut events : Vec<SMBEvent> = Vec::new();
800
801 match parse_smb_trans_request_record(r.data, r) {
802 Ok((_, rd)) => {
803 SCLogDebug!("TRANS request {:?}", rd);
804
805 /* if we have a fid, store it so the response can pick it up */
806 let mut pipe_dcerpc = false;
807 if rd.pipe != None {
808 let pipe = rd.pipe.unwrap();
809 state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID),
810 pipe.fid.to_vec());
811
812 let mut frankenfid = pipe.fid.to_vec();
813 frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id));
814
815 let (_filename, is_dcerpc) = match state.get_service_for_guid(&frankenfid) {
816 (n, x) => (n, x),
817 };
818 SCLogDebug!("smb1_trans_request_record: name {} is_dcerpc {}",
819 _filename, is_dcerpc);
820 pipe_dcerpc = is_dcerpc;
821 }
822
823 if pipe_dcerpc {
824 SCLogDebug!("SMBv1 TRANS TO PIPE");
825 let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
826 let vercmd = SMBVerCmdStat::new1(r.command);
827 smb_write_dcerpc_record(state, vercmd, hdr, &rd.data.data);
828 }
829 },
830 _ => {
831 events.push(SMBEvent::MalformedData);
832 },
833 }
834 smb1_request_record_generic(state, r, events);
835 }
836
smb1_trans_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)837 pub fn smb1_trans_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
838 {
839 let mut events : Vec<SMBEvent> = Vec::new();
840
841 match parse_smb_trans_response_record(r.data) {
842 Ok((_, rd)) => {
843 SCLogDebug!("TRANS response {:?}", rd);
844
845 // see if we have a stored fid
846 let fid = match state.ssn2vec_map.remove(
847 &SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID)) {
848 Some(f) => f,
849 None => Vec::new(),
850 };
851 SCLogDebug!("FID {:?}", fid);
852
853 let mut frankenfid = fid.to_vec();
854 frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id));
855
856 let (_filename, is_dcerpc) = match state.get_service_for_guid(&frankenfid) {
857 (n, x) => (n, x),
858 };
859 SCLogDebug!("smb1_trans_response_record: name {} is_dcerpc {}",
860 _filename, is_dcerpc);
861
862 // if we get status 'BUFFER_OVERFLOW' this is only a part of
863 // the data. Store it in the ssn/tree for later use.
864 if r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
865 let key = SMBHashKeyHdrGuid::new(SMBCommonHdr::from1(r, SMBHDR_TYPE_TRANS_FRAG), fid);
866 SCLogDebug!("SMBv1/TRANS: queueing data for len {} key {:?}", rd.data.len(), key);
867 state.ssnguid2vec_map.insert(key, rd.data.to_vec());
868 } else if is_dcerpc {
869 SCLogDebug!("SMBv1 TRANS TO PIPE");
870 let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
871 let vercmd = SMBVerCmdStat::new1_with_ntstatus(r.command, r.nt_status);
872 smb_read_dcerpc_record(state, vercmd, hdr, &fid, &rd.data);
873 }
874 },
875 _ => {
876 events.push(SMBEvent::MalformedData);
877 },
878 }
879
880 // generic tx as well. Set events if needed.
881 smb1_response_record_generic(state, r, events);
882 }
883
884 /// Handle WRITE, WRITE_ANDX, WRITE_AND_CLOSE request records
smb1_write_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)885 pub fn smb1_write_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
886 {
887 let mut events : Vec<SMBEvent> = Vec::new();
888
889 let result = if r.command == SMB1_COMMAND_WRITE_ANDX {
890 parse_smb1_write_andx_request_record(r.data)
891 } else if r.command == SMB1_COMMAND_WRITE {
892 parse_smb1_write_request_record(r.data)
893 } else {
894 parse_smb1_write_and_close_request_record(r.data)
895 };
896 match result {
897 Ok((_, rd)) => {
898 SCLogDebug!("SMBv1: write andx => {:?}", rd);
899
900 let mut file_fid = rd.fid.to_vec();
901 file_fid.extend_from_slice(&u32_as_bytes(r.ssn_id));
902 SCLogDebug!("SMBv1 WRITE: FID {:?} offset {}",
903 file_fid, rd.offset);
904
905 let file_name = match state.guid2name_map.get(&file_fid) {
906 Some(n) => n.to_vec(),
907 None => b"<unknown>".to_vec(),
908 };
909 let mut set_event_fileoverlap = false;
910 let found = match state.get_file_tx_by_fuid(&file_fid, STREAM_TOSERVER) {
911 Some((tx, files, flags)) => {
912 let file_id : u32 = tx.id as u32;
913 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
914 if rd.offset < tdf.file_tracker.tracked {
915 set_event_fileoverlap = true;
916 }
917 filetracker_newchunk(&mut tdf.file_tracker, files, flags,
918 &file_name, rd.data, rd.offset,
919 rd.len, 0, false, &file_id);
920 SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id);
921 }
922 true
923 },
924 None => { false },
925 };
926 if !found {
927 let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
928 let (share_name, is_pipe) = match state.ssn2tree_map.get(&tree_key) {
929 Some(n) => (n.name.to_vec(), n.is_pipe),
930 None => (Vec::new(), false),
931 };
932 if is_pipe {
933 SCLogDebug!("SMBv1 WRITE TO PIPE");
934 let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
935 let vercmd = SMBVerCmdStat::new1_with_ntstatus(r.command, r.nt_status);
936 smb_write_dcerpc_record(state, vercmd, hdr, &rd.data);
937 } else {
938 let (tx, files, flags) = state.new_file_tx(&file_fid, &file_name, STREAM_TOSERVER);
939 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
940 let file_id : u32 = tx.id as u32;
941 SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id);
942 if rd.offset < tdf.file_tracker.tracked {
943 set_event_fileoverlap = true;
944 }
945 filetracker_newchunk(&mut tdf.file_tracker, files, flags,
946 &file_name, rd.data, rd.offset,
947 rd.len, 0, false, &file_id);
948 tdf.share_name = share_name;
949 }
950 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_WRITE_ANDX);
951 }
952 }
953 if set_event_fileoverlap {
954 state.set_event(SMBEvent::FileOverlap);
955 }
956
957 state.set_file_left(STREAM_TOSERVER, rd.len, rd.data.len() as u32, file_fid.to_vec());
958
959 if r.command == SMB1_COMMAND_WRITE_AND_CLOSE {
960 SCLogDebug!("closing FID {:?}", file_fid);
961 smb1_close_file(state, &file_fid);
962 }
963 },
964 _ => {
965 events.push(SMBEvent::MalformedData);
966 },
967 }
968 smb1_request_record_generic(state, r, events);
969 }
970
smb1_read_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)971 pub fn smb1_read_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>)
972 {
973 let mut events : Vec<SMBEvent> = Vec::new();
974
975 if r.nt_status == SMB_NTSTATUS_SUCCESS {
976 match parse_smb_read_andx_response_record(r.data) {
977 Ok((_, rd)) => {
978 SCLogDebug!("SMBv1: read response => {:?}", rd);
979
980 let fid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_OFFSET);
981 let (offset, file_fid) = match state.ssn2vecoffset_map.remove(&fid_key) {
982 Some(o) => (o.offset, o.guid),
983 None => {
984 SCLogDebug!("SMBv1 READ response: reply to unknown request: left {} {:?}",
985 rd.len - rd.data.len() as u32, rd);
986 state.set_skip(STREAM_TOCLIENT, rd.len, rd.data.len() as u32);
987 return;
988 },
989 };
990 SCLogDebug!("SMBv1 READ: FID {:?} offset {}", file_fid, offset);
991
992 let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
993 let (is_pipe, share_name) = match state.ssn2tree_map.get(&tree_key) {
994 Some(n) => (n.is_pipe, n.name.to_vec()),
995 _ => { (false, Vec::new()) },
996 };
997 if !is_pipe {
998 let file_name = match state.guid2name_map.get(&file_fid) {
999 Some(n) => n.to_vec(),
1000 None => Vec::new(),
1001 };
1002 let mut set_event_fileoverlap = false;
1003 let found = match state.get_file_tx_by_fuid(&file_fid, STREAM_TOCLIENT) {
1004 Some((tx, files, flags)) => {
1005 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
1006 let file_id : u32 = tx.id as u32;
1007 SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id);
1008 if offset < tdf.file_tracker.tracked {
1009 set_event_fileoverlap = true;
1010 }
1011 filetracker_newchunk(&mut tdf.file_tracker, files, flags,
1012 &file_name, rd.data, offset,
1013 rd.len, 0, false, &file_id);
1014 }
1015 true
1016 },
1017 None => { false },
1018 };
1019 if !found {
1020 let (tx, files, flags) = state.new_file_tx(&file_fid, &file_name, STREAM_TOCLIENT);
1021 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
1022 let file_id : u32 = tx.id as u32;
1023 SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id);
1024 if offset < tdf.file_tracker.tracked {
1025 set_event_fileoverlap = true;
1026 }
1027 filetracker_newchunk(&mut tdf.file_tracker, files, flags,
1028 &file_name, rd.data, offset,
1029 rd.len, 0, false, &file_id);
1030 tdf.share_name = share_name;
1031 }
1032 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_READ_ANDX);
1033 }
1034 if set_event_fileoverlap {
1035 state.set_event(SMBEvent::FileOverlap);
1036 }
1037 } else {
1038 SCLogDebug!("SMBv1 READ response from PIPE");
1039 let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
1040 let vercmd = SMBVerCmdStat::new1(r.command);
1041
1042 // hack: we store fid with ssn id mixed in, but here we want the
1043 // real thing instead.
1044 let pure_fid = if file_fid.len() > 2 { &file_fid[0..2] } else { &[] };
1045 smb_read_dcerpc_record(state, vercmd, hdr, &pure_fid, &rd.data);
1046 }
1047
1048 state.set_file_left(STREAM_TOCLIENT, rd.len, rd.data.len() as u32, file_fid.to_vec());
1049 }
1050 _ => {
1051 events.push(SMBEvent::MalformedData);
1052 },
1053 }
1054 }
1055
1056 // generic tx as well. Set events if needed.
1057 smb1_response_record_generic(state, r, events);
1058 }
1059
1060 /// create a tx for a command / response pair if we're
1061 /// configured to do so, or if this is a tx especially
1062 /// for setting an event.
smb1_request_record_generic<'b>(state: &mut SMBState, r: &SmbRecord<'b>, events: Vec<SMBEvent>)1063 fn smb1_request_record_generic<'b>(state: &mut SMBState, r: &SmbRecord<'b>, events: Vec<SMBEvent>) {
1064 if smb1_create_new_tx(r.command) || events.len() > 0 {
1065 let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
1066 let tx = state.new_generic_tx(1, r.command as u16, tx_key);
1067 tx.set_events(events);
1068 }
1069 }
1070
1071 /// update or create a tx for a command / reponse pair based
1072 /// on the response. We only create a tx for the response side
1073 /// if we didn't already update a tx, and we have to set events
smb1_response_record_generic<'b>(state: &mut SMBState, r: &SmbRecord<'b>, events: Vec<SMBEvent>)1074 fn smb1_response_record_generic<'b>(state: &mut SMBState, r: &SmbRecord<'b>, events: Vec<SMBEvent>) {
1075 let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
1076 match state.get_generic_tx(1, r.command as u16, &tx_key) {
1077 Some(tx) => {
1078 tx.request_done = true;
1079 tx.response_done = true;
1080 SCLogDebug!("tx {} cmd {} is done", tx.id, r.command);
1081 tx.set_status(r.nt_status, r.is_dos_error);
1082 tx.set_events(events);
1083 return;
1084 },
1085 None => {},
1086 }
1087 if events.len() > 0 {
1088 let tx = state.new_generic_tx(1, r.command as u16, tx_key);
1089 tx.request_done = true;
1090 tx.response_done = true;
1091 SCLogDebug!("tx {} cmd {} is done", tx.id, r.command);
1092 tx.set_status(r.nt_status, r.is_dos_error);
1093 tx.set_events(events);
1094 }
1095 }
1096