Coverage Report

Created: 2026-01-16 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/rust/src/smb/smb2.rs
Line
Count
Source
1
/* Copyright (C) 2017-2022 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
use nom7::Err;
19
20
use crate::core::*;
21
22
use crate::smb::smb::*;
23
use crate::smb::smb2_records::*;
24
use crate::smb::smb2_session::*;
25
use crate::smb::smb2_ioctl::*;
26
use crate::smb::dcerpc::*;
27
use crate::smb::events::*;
28
use crate::smb::files::*;
29
use crate::smb::smb_status::*;
30
31
pub const SMB2_COMMAND_NEGOTIATE_PROTOCOL:      u16 = 0;
32
pub const SMB2_COMMAND_SESSION_SETUP:           u16 = 1;
33
pub const SMB2_COMMAND_SESSION_LOGOFF:          u16 = 2;
34
pub const SMB2_COMMAND_TREE_CONNECT:            u16 = 3;
35
pub const SMB2_COMMAND_TREE_DISCONNECT:         u16 = 4;
36
pub const SMB2_COMMAND_CREATE:                  u16 = 5;
37
pub const SMB2_COMMAND_CLOSE:                   u16 = 6;
38
pub const SMB2_COMMAND_FLUSH:                   u16 = 7;
39
pub const SMB2_COMMAND_READ:                    u16 = 8;
40
pub const SMB2_COMMAND_WRITE:                   u16 = 9;
41
pub const SMB2_COMMAND_LOCK:                    u16 = 10;
42
pub const SMB2_COMMAND_IOCTL:                   u16 = 11;
43
pub const SMB2_COMMAND_CANCEL:                  u16 = 12;
44
pub const SMB2_COMMAND_KEEPALIVE:               u16 = 13;
45
pub const SMB2_COMMAND_FIND:                    u16 = 14;
46
pub const SMB2_COMMAND_CHANGE_NOTIFY:           u16 = 15;
47
pub const SMB2_COMMAND_GET_INFO:                u16 = 16;
48
pub const SMB2_COMMAND_SET_INFO:                u16 = 17;
49
pub const SMB2_COMMAND_OPLOCK_BREAK:            u16 = 18;
50
51
37.2k
pub fn smb2_command_string(c: u16) -> String {
52
37.2k
    match c {
53
4.74k
        SMB2_COMMAND_NEGOTIATE_PROTOCOL     => "SMB2_COMMAND_NEGOTIATE_PROTOCOL",
54
3.28k
        SMB2_COMMAND_SESSION_SETUP          => "SMB2_COMMAND_SESSION_SETUP",
55
95
        SMB2_COMMAND_SESSION_LOGOFF         => "SMB2_COMMAND_SESSION_LOGOFF",
56
5.36k
        SMB2_COMMAND_TREE_CONNECT           => "SMB2_COMMAND_TREE_CONNECT",
57
5.80k
        SMB2_COMMAND_TREE_DISCONNECT        => "SMB2_COMMAND_TREE_DISCONNECT",
58
4.75k
        SMB2_COMMAND_CREATE                 => "SMB2_COMMAND_CREATE",
59
3.61k
        SMB2_COMMAND_CLOSE                  => "SMB2_COMMAND_CLOSE",
60
1.07k
        SMB2_COMMAND_READ                   => "SMB2_COMMAND_READ",
61
29
        SMB2_COMMAND_FLUSH                  => "SMB2_COMMAND_FLUSH",
62
902
        SMB2_COMMAND_WRITE                  => "SMB2_COMMAND_WRITE",
63
0
        SMB2_COMMAND_LOCK                   => "SMB2_COMMAND_LOCK",
64
4.70k
        SMB2_COMMAND_IOCTL                  => "SMB2_COMMAND_IOCTL",
65
0
        SMB2_COMMAND_CANCEL                 => "SMB2_COMMAND_CANCEL",
66
596
        SMB2_COMMAND_KEEPALIVE              => "SMB2_COMMAND_KEEPALIVE",
67
1.18k
        SMB2_COMMAND_FIND                   => "SMB2_COMMAND_FIND",
68
62
        SMB2_COMMAND_CHANGE_NOTIFY          => "SMB2_COMMAND_CHANGE_NOTIFY",
69
0
        SMB2_COMMAND_GET_INFO               => "SMB2_COMMAND_GET_INFO",
70
41
        SMB2_COMMAND_SET_INFO               => "SMB2_COMMAND_SET_INFO",
71
54
        SMB2_COMMAND_OPLOCK_BREAK           => "SMB2_COMMAND_OPLOCK_BREAK",
72
931
        _ => { return (c).to_string(); },
73
36.3k
    }.to_string()
74
75
37.2k
}
76
77
227k
pub fn smb2_dialect_string(d: u16) -> String {
78
227k
    match d {
79
25.3k
        0x0202 => "2.02",
80
5.85k
        0x0210 => "2.10",
81
3.85k
        0x0222 => "2.22",
82
3.85k
        0x0224 => "2.24",
83
1.96k
        0x02ff => "2.??",
84
4.83k
        0x0300 => "3.00",
85
15.1k
        0x0302 => "3.02",
86
4.29k
        0x0310 => "3.10",
87
5.50k
        0x0311 => "3.11",
88
156k
        _ => { return (d).to_string(); },
89
70.6k
    }.to_string()
90
227k
}
91
92
// later we'll use this to determine if we need to
93
// track a ssn per type
94
251k
fn smb2_create_new_tx(cmd: u16) -> bool {
95
251k
    match cmd {
96
        SMB2_COMMAND_READ |
97
        SMB2_COMMAND_WRITE |
98
        SMB2_COMMAND_GET_INFO |
99
187k
        SMB2_COMMAND_SET_INFO => { false },
100
64.1k
        _ => { true },
101
    }
102
251k
}
103
104
34.5k
fn smb2_read_response_record_generic(state: &mut SMBState, r: &Smb2Record)
105
{
106
34.5k
    if smb2_create_new_tx(r.command) {
107
0
        let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
108
0
        let tx = state.get_generic_tx(2, r.command, &tx_hdr);
109
0
        if let Some(tx) = tx {
110
0
            tx.set_status(r.nt_status, false);
111
0
            tx.response_done = true;
112
0
        }
113
34.5k
    }
114
34.5k
}
115
116
34.5k
pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_remaining: u32)
117
{
118
34.5k
    let max_queue_size = unsafe { SMB_CFG_MAX_READ_QUEUE_SIZE };
119
34.5k
    let max_queue_cnt = unsafe { SMB_CFG_MAX_READ_QUEUE_CNT };
120
121
34.5k
    smb2_read_response_record_generic(state, r);
122
123
34.5k
    match parse_smb2_response_read(r.data) {
124
29.5k
        Ok((_, rd)) => {
125
29.5k
            if rd.len - rd.data.len() as u32 > nbss_remaining {
126
                // Record claims more bytes than are in NBSS record...
127
2.49k
                state.set_event(SMBEvent::ReadResponseTooLarge);
128
                // Skip the remaining bytes of the record.
129
2.49k
                state.set_skip(Direction::ToClient, nbss_remaining);
130
2.49k
                return;
131
27.0k
            }
132
27.0k
            if r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
133
7.36k
                SCLogDebug!("SMBv2/READ: incomplete record, expecting a follow up");
134
7.36k
                // fall through
135
7.36k
136
19.6k
            } else if r.nt_status != SMB_NTSTATUS_SUCCESS {
137
                SCLogDebug!("SMBv2: read response error code received: skip record");
138
295
                state.set_skip(Direction::ToClient, nbss_remaining);
139
295
                return;
140
19.3k
            }
141
142
26.7k
            if (state.max_read_size != 0 && rd.len > state.max_read_size) ||
143
26.3k
               (unsafe { SMB_CFG_MAX_READ_SIZE != 0 && SMB_CFG_MAX_READ_SIZE < rd.len })
144
            {
145
399
                state.set_event(SMBEvent::ReadResponseTooLarge);
146
399
                state.set_skip(Direction::ToClient, nbss_remaining);
147
399
                return;
148
26.3k
            }
149
150
            SCLogDebug!("SMBv2: read response => {:?}", rd);
151
152
            // get the request info. If we don't have it, there is nothing
153
            // we can do except skip this record.
154
26.3k
            let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET);
155
26.3k
            let (offset, file_guid) = match state.ssn2vecoffset_map.remove(&guid_key) {
156
21.8k
                Some(o) => (o.offset, o.guid),
157
                None => {
158
                    SCLogDebug!("SMBv2 READ response: reply to unknown request {:?}",rd);
159
4.50k
                    state.set_skip(Direction::ToClient, nbss_remaining);
160
4.50k
                    return;
161
                },
162
            };
163
            SCLogDebug!("SMBv2 READ: GUID {:?} offset {}", file_guid, offset);
164
165
21.8k
            let mut set_event_fileoverlap = false;
166
            // look up existing tracker and if we have it update it
167
21.8k
            let found = match state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToClient) {
168
2.55k
                Some(tx) => {
169
2.55k
                    if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
170
2.55k
                        let file_id : u32 = tx.id as u32;
171
2.55k
                        if offset < tdf.file_tracker.tracked {
172
1.14k
                            set_event_fileoverlap = true;
173
1.40k
                        }
174
2.55k
                        if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + rd.len as u64 > max_queue_size.into() {
175
0
                            state.set_event(SMBEvent::ReadQueueSizeExceeded);
176
0
                            state.set_skip(Direction::ToClient, nbss_remaining);
177
2.55k
                        } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize {
178
0
                            state.set_event(SMBEvent::ReadQueueCntExceeded);
179
0
                            state.set_skip(Direction::ToClient, nbss_remaining);
180
2.55k
                        } else {
181
2.55k
                            filetracker_newchunk(&mut tdf.file_tracker,
182
2.55k
                                    &tdf.file_name, rd.data, offset,
183
2.55k
                                    rd.len, false, &file_id);
184
2.55k
                        }
185
0
                    }
186
2.55k
                    true
187
                },
188
19.2k
                None => { false },
189
            };
190
            SCLogDebug!("existing file tx? {}", found);
191
21.8k
            if !found {
192
19.2k
                let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
193
19.2k
                let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) {
194
9.82k
                    Some(n) => (n.name.to_vec(), n.is_pipe),
195
9.45k
                    _ => { (Vec::new(), false) },
196
                };
197
19.2k
                let mut is_dcerpc = if is_pipe || share_name.is_empty() {
198
18.7k
                    state.get_service_for_guid(&file_guid).1
199
                } else {
200
515
                    false
201
                };
202
                SCLogDebug!("SMBv2/READ: share_name {:?} is_pipe {} is_dcerpc {}",
203
                        share_name, is_pipe, is_dcerpc);
204
205
19.2k
                if share_name.is_empty() && !is_pipe {
206
                    SCLogDebug!("SMBv2/READ: no tree connect seen, we don't know if we are a pipe");
207
208
9.76k
                    if smb_dcerpc_probe(rd.data) {
209
                        SCLogDebug!("SMBv2/READ: looks like dcerpc");
210
                        // insert fake tree to assist in follow up lookups
211
6.04k
                        let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true);
212
6.04k
                        state.ssn2tree_map.insert(tree_key, tree);
213
6.04k
                        if !is_dcerpc {
214
2.23k
                            state.guid2name_map.insert(file_guid.to_vec(), b"suricata::dcerpc".to_vec());
215
3.81k
                        }
216
6.04k
                        is_pipe = true;
217
6.04k
                        is_dcerpc = true;
218
3.71k
                    } else {
219
3.71k
                        SCLogDebug!("SMBv2/READ: not DCERPC");
220
3.71k
                    }
221
9.51k
                }
222
223
19.2k
                if is_pipe && is_dcerpc {
224
13.7k
                    SCLogDebug!("SMBv2 DCERPC read");
225
13.7k
                    let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
226
13.7k
                    let vercmd = SMBVerCmdStat::new2_with_ntstatus(SMB2_COMMAND_READ, r.nt_status);
227
13.7k
                    smb_read_dcerpc_record(state, vercmd, hdr, &file_guid, rd.data);
228
13.7k
                } else if is_pipe {
229
1.34k
                    SCLogDebug!("non-DCERPC pipe");
230
1.34k
                    state.set_skip(Direction::ToClient, nbss_remaining);
231
1.34k
                } else {
232
4.23k
                    let file_name = match state.guid2name_map.get(&file_guid) {
233
883
                        Some(n) => { n.to_vec() }
234
3.34k
                        None => { b"<unknown>".to_vec() }
235
                    };
236
237
4.23k
                    let tx = state.new_file_tx(&file_guid, &file_name, Direction::ToClient);
238
4.23k
                    tx.vercmd.set_smb2_cmd(SMB2_COMMAND_READ);
239
4.23k
                    tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
240
4.23k
                            r.session_id, r.tree_id, 0); // TODO move into new_file_tx
241
4.23k
                    if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
242
4.23k
                        tdf.share_name = share_name;
243
4.23k
                        let file_id : u32 = tx.id as u32;
244
4.23k
                        if offset < tdf.file_tracker.tracked {
245
0
                            set_event_fileoverlap = true;
246
4.23k
                        }
247
4.23k
                        if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + rd.len as u64 > max_queue_size.into() {
248
0
                            state.set_event(SMBEvent::ReadQueueSizeExceeded);
249
0
                            state.set_skip(Direction::ToClient, nbss_remaining);
250
4.23k
                        } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize {
251
0
                            state.set_event(SMBEvent::ReadQueueCntExceeded);
252
0
                            state.set_skip(Direction::ToClient, nbss_remaining);
253
4.23k
                        } else {
254
4.23k
                            filetracker_newchunk(&mut tdf.file_tracker,
255
4.23k
                                    &file_name, rd.data, offset,
256
4.23k
                                    rd.len, false, &file_id);
257
4.23k
                        }
258
0
                    }
259
                }
260
2.55k
            }
261
262
21.8k
            if set_event_fileoverlap {
263
1.14k
                state.set_event(SMBEvent::FileOverlap);
264
20.6k
            }
265
21.8k
            state.set_file_left(Direction::ToClient, rd.len, rd.data.len() as u32, file_guid.to_vec());
266
        }
267
5.04k
        _ => {
268
5.04k
            SCLogDebug!("SMBv2: failed to parse read response");
269
5.04k
            state.set_event(SMBEvent::MalformedData);
270
5.04k
        }
271
    }
272
34.5k
}
273
274
110k
pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_remaining: u32)
275
{
276
110k
    let max_queue_size = unsafe { SMB_CFG_MAX_WRITE_QUEUE_SIZE };
277
110k
    let max_queue_cnt = unsafe { SMB_CFG_MAX_WRITE_QUEUE_CNT };
278
279
    SCLogDebug!("SMBv2/WRITE: request record");
280
110k
    if smb2_create_new_tx(r.command) {
281
0
        let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
282
0
        let tx = state.new_generic_tx(2, r.command, tx_key);
283
0
        tx.request_done = true;
284
110k
    }
285
110k
    match parse_smb2_request_write(r.data) {
286
105k
        Ok((_, wr)) => {
287
105k
            if wr.wr_len - wr.data.len() as u32 > nbss_remaining {
288
                // Record claims more bytes than are in NBSS record...
289
13.7k
                state.set_event(SMBEvent::WriteRequestTooLarge);
290
                // Skip the remaining bytes of the record.
291
13.7k
                state.set_skip(Direction::ToServer, nbss_remaining);
292
13.7k
                return;
293
92.0k
            }
294
92.0k
            if (state.max_write_size != 0 && wr.wr_len > state.max_write_size) ||
295
90.0k
               (unsafe { SMB_CFG_MAX_WRITE_SIZE != 0 && SMB_CFG_MAX_WRITE_SIZE < wr.wr_len }) {
296
1.95k
                state.set_event(SMBEvent::WriteRequestTooLarge);
297
1.95k
                state.set_skip(Direction::ToServer, nbss_remaining);
298
1.95k
                return;
299
90.0k
            }
300
301
            /* update key-guid map */
302
90.0k
            let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GUID);
303
90.0k
            state.ssn2vec_map.insert(guid_key, wr.guid.to_vec());
304
305
90.0k
            let file_guid = wr.guid.to_vec();
306
90.0k
            let file_name = match state.guid2name_map.get(&file_guid) {
307
54.6k
                Some(n) => n.to_vec(),
308
35.3k
                None => Vec::new(),
309
            };
310
311
90.0k
            let mut set_event_fileoverlap = false;
312
90.0k
            let found = match state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToServer) {
313
9.28k
                Some(tx) => {
314
9.28k
                    if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
315
9.28k
                        let file_id : u32 = tx.id as u32;
316
9.28k
                        if wr.wr_offset < tdf.file_tracker.tracked {
317
764
                            set_event_fileoverlap = true;
318
8.51k
                        }
319
9.28k
                        if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + wr.wr_len as u64 > max_queue_size.into() {
320
0
                            state.set_event(SMBEvent::WriteQueueSizeExceeded);
321
0
                            state.set_skip(Direction::ToServer, nbss_remaining);
322
9.28k
                        } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize {
323
0
                            state.set_event(SMBEvent::WriteQueueCntExceeded);
324
0
                            state.set_skip(Direction::ToServer, nbss_remaining);
325
9.28k
                        } else {
326
9.28k
                            filetracker_newchunk(&mut tdf.file_tracker,
327
9.28k
                                    &file_name, wr.data, wr.wr_offset,
328
9.28k
                                    wr.wr_len, false, &file_id);
329
9.28k
                        }
330
0
                    }
331
9.28k
                    true
332
                },
333
80.7k
                None => { false },
334
            };
335
90.0k
            if !found {
336
80.7k
                let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
337
80.7k
                let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) {
338
51.7k
                    Some(n) => { (n.name.to_vec(), n.is_pipe) },
339
29.0k
                    _ => { (Vec::new(), false) },
340
                };
341
80.7k
                let mut is_dcerpc = if is_pipe || share_name.is_empty() {
342
80.3k
                    state.get_service_for_guid(wr.guid).1
343
                } else {
344
454
                    false
345
                };
346
                SCLogDebug!("SMBv2/WRITE: share_name {:?} is_pipe {} is_dcerpc {}",
347
                        share_name, is_pipe, is_dcerpc);
348
349
                // if we missed the TREE connect we can't be sure if 'is_dcerpc' is correct
350
80.7k
                if share_name.is_empty() && !is_pipe {
351
                    SCLogDebug!("SMBv2/WRITE: no tree connect seen, we don't know if we are a pipe");
352
353
29.3k
                    if smb_dcerpc_probe(wr.data) {
354
                        SCLogDebug!("SMBv2/WRITE: looks like we have dcerpc");
355
356
16.2k
                        let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true);
357
16.2k
                        state.ssn2tree_map.insert(tree_key, tree);
358
16.2k
                        if !is_dcerpc {
359
8.71k
                            state.guid2name_map.insert(file_guid.to_vec(),
360
8.71k
                                    b"suricata::dcerpc".to_vec());
361
8.71k
                        }
362
16.2k
                        is_pipe = true;
363
16.2k
                        is_dcerpc = true;
364
13.0k
                    } else {
365
13.0k
                        SCLogDebug!("SMBv2/WRITE: not DCERPC");
366
13.0k
                    }
367
51.4k
                }
368
80.7k
                if is_pipe && is_dcerpc {
369
61.4k
                    SCLogDebug!("SMBv2 DCERPC write");
370
61.4k
                    let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
371
61.4k
                    let vercmd = SMBVerCmdStat::new2(SMB2_COMMAND_WRITE);
372
61.4k
                    smb_write_dcerpc_record(state, vercmd, hdr, wr.data);
373
61.4k
                } else if is_pipe {
374
5.76k
                    SCLogDebug!("non-DCERPC pipe: skip rest of the record");
375
5.76k
                    state.set_skip(Direction::ToServer, nbss_remaining);
376
5.76k
                } else {
377
13.5k
                    let tx = state.new_file_tx(&file_guid, &file_name, Direction::ToServer);
378
13.5k
                    tx.vercmd.set_smb2_cmd(SMB2_COMMAND_WRITE);
379
13.5k
                    tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
380
13.5k
                            r.session_id, r.tree_id, 0); // TODO move into new_file_tx
381
13.5k
                    if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
382
13.5k
                        let file_id : u32 = tx.id as u32;
383
13.5k
                        if wr.wr_offset < tdf.file_tracker.tracked {
384
0
                            set_event_fileoverlap = true;
385
13.5k
                        }
386
387
13.5k
                        if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + wr.wr_len as u64 > max_queue_size.into() {
388
0
                            state.set_event(SMBEvent::WriteQueueSizeExceeded);
389
0
                            state.set_skip(Direction::ToServer, nbss_remaining);
390
13.5k
                        } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize {
391
0
                            state.set_event(SMBEvent::WriteQueueCntExceeded);
392
0
                            state.set_skip(Direction::ToServer, nbss_remaining);
393
13.5k
                        } else {
394
13.5k
                            filetracker_newchunk(&mut tdf.file_tracker,
395
13.5k
                                    &file_name, wr.data, wr.wr_offset,
396
13.5k
                                    wr.wr_len, false, &file_id);
397
13.5k
                        }
398
0
                    }
399
                }
400
9.28k
            }
401
402
90.0k
            if set_event_fileoverlap {
403
764
                state.set_event(SMBEvent::FileOverlap);
404
89.3k
            }
405
90.0k
            state.set_file_left(Direction::ToServer, wr.wr_len, wr.data.len() as u32, file_guid.to_vec());
406
        },
407
4.79k
        _ => {
408
4.79k
            state.set_event(SMBEvent::MalformedData);
409
4.79k
        },
410
    }
411
110k
}
412
413
375k
pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record)
414
{
415
    SCLogDebug!("SMBv2 request record, command {} tree {} session {}",
416
            &smb2_command_string(r.command), r.tree_id, r.session_id);
417
418
375k
    let mut events : Vec<SMBEvent> = Vec::new();
419
420
375k
    let have_tx = match r.command {
421
        SMB2_COMMAND_SET_INFO => {
422
            SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", r);
423
24.6k
            let have_si_tx = match parse_smb2_request_setinfo(r.data) {
424
13.1k
                Ok((_, rd)) => {
425
                    SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", rd);
426
427
13.1k
                    match rd.data {
428
1.59k
                        Smb2SetInfoRequestData::RENAME(ref ren) => {
429
1.59k
                            let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
430
1.59k
                            let mut newname = ren.name.to_vec();
431
16.4k
                            newname.retain(|&i|i != 0x00);
432
1.59k
                            let oldname = match state.guid2name_map.get(rd.guid) {
433
213
                                Some(n) => { n.to_vec() },
434
1.37k
                                None => { b"<unknown>".to_vec() },
435
                            };
436
1.59k
                            let tx = state.new_rename_tx(rd.guid.to_vec(), oldname, newname);
437
1.59k
                            tx.hdr = tx_hdr;
438
1.59k
                            tx.request_done = true;
439
1.59k
                            tx.vercmd.set_smb2_cmd(SMB2_COMMAND_SET_INFO);
440
1.59k
                            true
441
                        }
442
10.3k
                        Smb2SetInfoRequestData::DISPOSITION(ref dis) => {
443
10.3k
                            let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
444
10.3k
                            let fname = match state.guid2name_map.get(rd.guid) {
445
226
                                Some(n) => { n.to_vec() },
446
                                None => {
447
                                    // try to find latest created file in case of chained commands
448
10.1k
                                    let mut guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME);
449
10.1k
                                    if guid_key.msg_id == 0 {
450
194
                                        b"<unknown>".to_vec()
451
                                    } else {
452
9.92k
                                        guid_key.msg_id -= 1;
453
9.92k
                                        match state.ssn2vec_map.get(&guid_key) {
454
47
                                            Some(n) => { n.to_vec() },
455
9.87k
                                            None => { b"<unknown>".to_vec()},
456
                                        }
457
                                    }
458
                                },
459
                            };
460
10.3k
                            let tx = state.new_setfileinfo_tx(fname, rd.guid.to_vec(), rd.class as u16, rd.infolvl as u16, dis.delete);
461
10.3k
                            tx.hdr = tx_hdr;
462
10.3k
                            tx.request_done = true;
463
10.3k
                            tx.vercmd.set_smb2_cmd(SMB2_COMMAND_SET_INFO);
464
10.3k
                            true
465
                        }
466
1.19k
                        _ => false,
467
                    }
468
                },
469
11.4k
                Err(Err::Incomplete(_n)) => {
470
                    SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", _n);
471
11.4k
                    events.push(SMBEvent::MalformedData);
472
11.4k
                    false
473
                },
474
0
                Err(Err::Error(_e)) |
475
0
                Err(Err::Failure(_e)) => {
476
                    SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", _e);
477
0
                    events.push(SMBEvent::MalformedData);
478
0
                    false
479
                },
480
            };
481
24.6k
            have_si_tx
482
        },
483
        SMB2_COMMAND_IOCTL => {
484
21.2k
            smb2_ioctl_request_record(state, r);
485
21.2k
            true
486
        },
487
        SMB2_COMMAND_TREE_DISCONNECT => {
488
15.6k
            let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
489
15.6k
            state.ssn2tree_map.remove(&tree_key);
490
15.6k
            false
491
        }
492
        SMB2_COMMAND_NEGOTIATE_PROTOCOL => {
493
27.4k
            match parse_smb2_request_negotiate_protocol(r.data) {
494
13.1k
                Ok((_, rd)) => {
495
13.1k
                    let mut dialects : Vec<Vec<u8>> = Vec::new();
496
225k
                    for d in rd.dialects_vec {
497
212k
                        SCLogDebug!("dialect {:x} => {}", d, &smb2_dialect_string(d));
498
212k
                        let dvec = smb2_dialect_string(d).as_bytes().to_vec();
499
212k
                        dialects.push(dvec);
500
212k
                    }
501
502
13.1k
                    let found = match state.get_negotiate_tx(2) {
503
                        Some(_) => {
504
                            SCLogDebug!("WEIRD, should not have NEGOTIATE tx!");
505
4.09k
                            true
506
                        },
507
9.09k
                        None => { false },
508
                    };
509
13.1k
                    if !found {
510
9.09k
                        let tx = state.new_negotiate_tx(2);
511
9.09k
                        if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
512
9.09k
                            tdn.dialects2 = dialects;
513
9.09k
                            tdn.client_guid = Some(rd.client_guid.to_vec());
514
9.09k
                        }
515
9.09k
                        tx.request_done = true;
516
4.09k
                    }
517
13.1k
                    true
518
                },
519
                _ => {
520
14.2k
                    events.push(SMBEvent::MalformedData);
521
14.2k
                    false
522
                },
523
            }
524
        },
525
        SMB2_COMMAND_SESSION_SETUP => {
526
79.2k
            smb2_session_setup_request(state, r);
527
79.2k
            true
528
        },
529
        SMB2_COMMAND_TREE_CONNECT => {
530
18.8k
            match parse_smb2_request_tree_connect(r.data) {
531
15.7k
                Ok((_, tr)) => {
532
15.7k
                    let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE);
533
15.7k
                    let mut name_val = tr.share_name.to_vec();
534
10.3M
                    name_val.retain(|&i|i != 0x00);
535
15.7k
                    if name_val.len() > 1 {
536
14.5k
                        name_val = name_val[1..].to_vec();
537
14.5k
                    }
538
539
15.7k
                    let tx = state.new_treeconnect_tx(name_key, name_val);
540
15.7k
                    tx.request_done = true;
541
15.7k
                    tx.vercmd.set_smb2_cmd(SMB2_COMMAND_TREE_CONNECT);
542
15.7k
                    true
543
                }
544
                _ => {
545
3.15k
                    events.push(SMBEvent::MalformedData);
546
3.15k
                    false
547
                },
548
            }
549
        },
550
        SMB2_COMMAND_READ => {
551
24.9k
            match parse_smb2_request_read(r.data) {
552
17.8k
                Ok((_, rd)) => {
553
17.8k
                    if (state.max_read_size != 0 && rd.rd_len > state.max_read_size) ||
554
17.0k
                        (unsafe { SMB_CFG_MAX_READ_SIZE != 0 && SMB_CFG_MAX_READ_SIZE < rd.rd_len }) {
555
2.95k
                        events.push(SMBEvent::ReadRequestTooLarge);
556
14.9k
                    } else {
557
14.9k
                        SCLogDebug!("SMBv2 READ: GUID {:?} requesting {} bytes at offset {}",
558
14.9k
                                rd.guid, rd.rd_len, rd.rd_offset);
559
14.9k
560
14.9k
                        // store read guid,offset in map
561
14.9k
                        let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET);
562
14.9k
                        let guidoff = SMBFileGUIDOffset::new(rd.guid.to_vec(), rd.rd_offset);
563
14.9k
                        state.ssn2vecoffset_map.insert(guid_key, guidoff);
564
14.9k
                    }
565
                },
566
7.05k
                _ => {
567
7.05k
                    events.push(SMBEvent::MalformedData);
568
7.05k
                },
569
            }
570
24.9k
            false
571
        },
572
        SMB2_COMMAND_CREATE => {
573
41.5k
            match parse_smb2_request_create(r.data) {
574
28.2k
                Ok((_, cr)) => {
575
28.2k
                    let del = cr.create_options & 0x0000_1000 != 0;
576
28.2k
                    let dir = cr.create_options & 0x0000_0001 != 0;
577
578
                    SCLogDebug!("create_options {:08x}", cr.create_options);
579
580
28.2k
                    let name_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME);
581
28.2k
                    state.ssn2vec_map.insert(name_key, cr.data.to_vec());
582
583
28.2k
                    let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
584
28.2k
                    let tx = state.new_create_tx(cr.data,
585
28.2k
                            cr.disposition, del, dir, tx_hdr);
586
28.2k
                    tx.vercmd.set_smb2_cmd(r.command);
587
                    SCLogDebug!("TS CREATE TX {} created", tx.id);
588
28.2k
                    true
589
                },
590
                _ => {
591
13.3k
                    events.push(SMBEvent::MalformedData);
592
13.3k
                    false
593
                },
594
            }
595
        },
596
        SMB2_COMMAND_WRITE => {
597
99.1k
            smb2_write_request_record(state, r, 0);
598
99.1k
            true // write handling creates both file tx and generic tx
599
        },
600
        SMB2_COMMAND_CLOSE => {
601
7.85k
            match parse_smb2_request_close(r.data) {
602
6.98k
                Ok((_, cd)) => {
603
6.98k
                    let found_ts = match state.get_file_tx_by_fuid(cd.guid, Direction::ToServer) {
604
1.83k
                        Some(tx) => {
605
1.83k
                            if !tx.request_done {
606
1.09k
                                if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
607
1.09k
                                    filetracker_close(&mut tdf.file_tracker);
608
1.09k
                                }
609
733
                            }
610
1.83k
                            tx.request_done = true;
611
1.83k
                            tx.response_done = true;
612
1.83k
                            tx.set_status(SMB_NTSTATUS_SUCCESS, false);
613
1.83k
                            true
614
                        },
615
5.14k
                        None => { false },
616
                    };
617
6.98k
                    let found_tc = match state.get_file_tx_by_fuid(cd.guid, Direction::ToClient) {
618
1.24k
                        Some(tx) => {
619
1.24k
                            if !tx.request_done {
620
616
                                if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
621
616
                                    filetracker_close(&mut tdf.file_tracker);
622
616
                                }
623
633
                            }
624
1.24k
                            tx.request_done = true;
625
1.24k
                            tx.response_done = true;
626
1.24k
                            tx.set_status(SMB_NTSTATUS_SUCCESS, false);
627
1.24k
                            true
628
                        },
629
5.73k
                        None => { false },
630
                    };
631
6.98k
                    if !found_ts && !found_tc {
632
4.54k
                        SCLogDebug!("SMBv2: CLOSE(TS): no TX at GUID {:?}", cd.guid);
633
4.54k
                    }
634
                },
635
875
                _ => {
636
875
                    events.push(SMBEvent::MalformedData);
637
875
                },
638
            }
639
7.85k
            false
640
        },
641
        _ => {
642
14.4k
            false
643
        },
644
    };
645
    /* if we don't have a tx, create it here (maybe) */
646
375k
    if !have_tx && smb2_create_new_tx(r.command) {
647
64.1k
        let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
648
64.1k
        let tx = state.new_generic_tx(2, r.command, tx_key);
649
64.1k
        SCLogDebug!("TS TX {} command {} created with session_id {} tree_id {} message_id {}",
650
64.1k
                tx.id, r.command, r.session_id, r.tree_id, r.message_id);
651
64.1k
        tx.set_events(events);
652
310k
    }
653
375k
}
654
655
178k
pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record)
656
{
657
    SCLogDebug!("SMBv2 response record, command {} status {} tree {} session {} message {}",
658
            &smb2_command_string(r.command), r.nt_status,
659
            r.tree_id, r.session_id, r.message_id);
660
661
178k
    let mut events : Vec<SMBEvent> = Vec::new();
662
663
178k
    let have_tx = match r.command {
664
        SMB2_COMMAND_IOCTL => {
665
30.1k
            smb2_ioctl_response_record(state, r);
666
30.1k
            true
667
        },
668
        SMB2_COMMAND_SESSION_SETUP => {
669
15.9k
            smb2_session_setup_response(state, r);
670
15.9k
            true
671
        },
672
        SMB2_COMMAND_WRITE => {
673
4.03k
            if r.nt_status == SMB_NTSTATUS_SUCCESS {
674
3.45k
                match parse_smb2_response_write(r.data)
675
                {
676
2.02k
                    Ok((_, _wr)) => {
677
2.02k
                        SCLogDebug!("SMBv2: Write response => {:?}", _wr);
678
2.02k
679
2.02k
                        /* search key-guid map */
680
2.02k
                        let guid_key = SMBCommonHdr::new(SMBHDR_TYPE_GUID,
681
2.02k
                                r.session_id, r.tree_id, r.message_id);
682
2.02k
                        let _guid_vec = state.ssn2vec_map.remove(&guid_key).unwrap_or_default();
683
2.02k
                        SCLogDebug!("SMBv2 write response for GUID {:?}", _guid_vec);
684
2.02k
                    }
685
1.43k
                    _ => {
686
1.43k
                        events.push(SMBEvent::MalformedData);
687
1.43k
                    },
688
                }
689
571
            }
690
4.03k
            false // the request may have created a generic tx, so handle that here
691
        },
692
        SMB2_COMMAND_READ => {
693
39.0k
            if r.nt_status == SMB_NTSTATUS_SUCCESS ||
694
15.1k
               r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
695
27.4k
                smb2_read_response_record(state, r, 0);
696
27.4k
                false
697
698
11.5k
            } else if r.nt_status == SMB_NTSTATUS_END_OF_FILE {
699
                SCLogDebug!("SMBv2: read response => EOF");
700
701
7.14k
                let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET);
702
7.14k
                let file_guid = match state.ssn2vecoffset_map.remove(&guid_key) {
703
4.72k
                    Some(o) => o.guid,
704
                    _ => {
705
                        SCLogDebug!("SMBv2 READ response: reply to unknown request");
706
2.42k
                        Vec::new()
707
                    },
708
                };
709
7.14k
                let found = match state.get_file_tx_by_fuid(&file_guid, Direction::ToClient) {
710
3.71k
                    Some(tx) => {
711
3.71k
                        if !tx.request_done {
712
3.08k
                            if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
713
3.08k
                                filetracker_close(&mut tdf.file_tracker);
714
3.08k
                            }
715
624
                        }
716
3.71k
                        tx.set_status(r.nt_status, false);
717
3.71k
                        tx.request_done = true;
718
3.71k
                        false
719
                    },
720
3.43k
                    None => { false },
721
                };
722
7.14k
                if !found {
723
7.14k
                    SCLogDebug!("SMBv2 READ: no TX at GUID {:?}", file_guid);
724
7.14k
                }
725
7.14k
                false
726
            } else {
727
                SCLogDebug!("SMBv2 READ: status {}", r.nt_status);
728
4.39k
                false
729
            }
730
        },
731
        SMB2_COMMAND_CREATE => {
732
34.0k
            if r.nt_status == SMB_NTSTATUS_SUCCESS {
733
32.4k
                match parse_smb2_response_create(r.data) {
734
27.0k
                    Ok((_, cr)) => {
735
                        SCLogDebug!("SMBv2: Create response => {:?}", cr);
736
737
27.0k
                        let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME);
738
27.0k
                        if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) {
739
431k
                            p.retain(|&i|i != 0x00);
740
15.2k
                            state.guid2name_map.insert(cr.guid.to_vec(), p);
741
11.7k
                        } else {
742
11.7k
                            SCLogDebug!("SMBv2 response: GUID NOT FOUND");
743
11.7k
                        }
744
745
27.0k
                        let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
746
27.0k
                        if let Some(tx) = state.get_generic_tx(2, r.command, &tx_hdr) {
747
                            SCLogDebug!("tx {} with {}/{} marked as done",
748
                                    tx.id, r.command, &smb2_command_string(r.command));
749
8.34k
                            tx.set_status(r.nt_status, false);
750
8.34k
                            tx.response_done = true;
751
752
7.36k
                            if let Some(SMBTransactionTypeData::CREATE(ref mut tdn)) = tx.type_data {
753
7.36k
                                tdn.create_ts = cr.create_ts.as_unix();
754
7.36k
                                tdn.last_access_ts = cr.last_access_ts.as_unix();
755
7.36k
                                tdn.last_write_ts = cr.last_write_ts.as_unix();
756
7.36k
                                tdn.last_change_ts = cr.last_change_ts.as_unix();
757
7.36k
                                tdn.size = cr.size;
758
7.36k
                                tdn.guid = cr.guid.to_vec();
759
7.36k
                            }
760
18.7k
                        }
761
                    }
762
5.42k
                    _ => {
763
5.42k
                        events.push(SMBEvent::MalformedData);
764
5.42k
                    },
765
                }
766
32.4k
                true
767
            } else {
768
1.58k
                false
769
            }
770
        },
771
        SMB2_COMMAND_TREE_DISCONNECT => {
772
            // normally removed when processing request,
773
            // but in case we missed that try again here
774
7.19k
            let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
775
7.19k
            state.ssn2tree_map.remove(&tree_key);
776
7.19k
            false
777
        }
778
        SMB2_COMMAND_TREE_CONNECT => {
779
16.5k
            if r.nt_status == SMB_NTSTATUS_SUCCESS {
780
10.6k
                match parse_smb2_response_tree_connect(r.data) {
781
8.36k
                    Ok((_, tr)) => {
782
8.36k
                        let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE);
783
8.36k
                        let mut share_name = Vec::new();
784
8.36k
                        let is_pipe = tr.share_type == 2;
785
8.36k
                        let found = match state.get_treeconnect_tx(name_key) {
786
2.45k
                            Some(tx) => {
787
2.45k
                                if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
788
2.45k
                                    tdn.share_type = tr.share_type;
789
2.45k
                                    tdn.is_pipe = is_pipe;
790
2.45k
                                    tdn.tree_id = r.tree_id;
791
2.45k
                                    share_name = tdn.share_name.to_vec();
792
2.45k
                                }
793
                                // update hdr now that we have a tree_id
794
2.45k
                                tx.hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
795
2.45k
                                tx.response_done = true;
796
2.45k
                                tx.set_status(r.nt_status, false);
797
2.45k
                                true
798
                            },
799
5.91k
                            None => { false },
800
                        };
801
8.36k
                        if found {
802
2.45k
                            let tree = SMBTree::new(share_name.to_vec(), is_pipe);
803
2.45k
                            let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
804
2.45k
                            state.ssn2tree_map.insert(tree_key, tree);
805
5.91k
                        }
806
8.36k
                        true
807
                    }
808
                    _ => {
809
2.24k
                        events.push(SMBEvent::MalformedData);
810
2.24k
                        false
811
                    },
812
                }
813
            } else {
814
5.97k
                let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE);
815
5.97k
                let found = match state.get_treeconnect_tx(name_key) {
816
966
                    Some(tx) => {
817
966
                        tx.response_done = true;
818
966
                        tx.set_status(r.nt_status, false);
819
966
                        true
820
                    },
821
5.01k
                    None => { false },
822
                };
823
5.97k
                found
824
            }
825
        },
826
        SMB2_COMMAND_NEGOTIATE_PROTOCOL => {
827
18.6k
            let res = if r.nt_status == SMB_NTSTATUS_SUCCESS {
828
11.7k
                parse_smb2_response_negotiate_protocol(r.data)
829
            } else {
830
6.97k
                parse_smb2_response_negotiate_protocol_error(r.data)
831
            };
832
18.6k
            match res {
833
10.6k
                Ok((_, rd)) => {
834
                    SCLogDebug!("SERVER dialect => {}", &smb2_dialect_string(rd.dialect));
835
836
10.6k
                    let smb_cfg_max_read_size = unsafe { SMB_CFG_MAX_READ_SIZE };
837
10.6k
                    if smb_cfg_max_read_size != 0 && rd.max_read_size > smb_cfg_max_read_size {
838
3.81k
                        state.set_event(SMBEvent::NegotiateMaxReadSizeTooLarge);
839
6.78k
                    }
840
10.6k
                    let smb_cfg_max_write_size = unsafe { SMB_CFG_MAX_WRITE_SIZE };
841
10.6k
                    if smb_cfg_max_write_size != 0 && rd.max_write_size > smb_cfg_max_write_size {
842
3.82k
                        state.set_event(SMBEvent::NegotiateMaxWriteSizeTooLarge);
843
6.78k
                    }
844
845
10.6k
                    state.dialect = rd.dialect;
846
10.6k
                    state.max_read_size = rd.max_read_size;
847
10.6k
                    state.max_write_size = rd.max_write_size;
848
849
10.6k
                    let found2 = match state.get_negotiate_tx(2) {
850
1.04k
                        Some(tx) => {
851
1.04k
                            if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
852
1.04k
                                tdn.server_guid = rd.server_guid.to_vec();
853
1.04k
                            }
854
1.04k
                            tx.set_status(r.nt_status, false);
855
1.04k
                            tx.response_done = true;
856
1.04k
                            true
857
                        },
858
9.56k
                        None => { false },
859
                    };
860
                    // SMB2 response to SMB1 request?
861
10.6k
                    let found1 = !found2 && match state.get_negotiate_tx(1) {
862
862
                        Some(tx) => {
863
862
                            if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
864
862
                                tdn.server_guid = rd.server_guid.to_vec();
865
862
                            }
866
862
                            tx.set_status(r.nt_status, false);
867
862
                            tx.response_done = true;
868
862
                            true
869
                        },
870
8.70k
                        None => { false },
871
                    };
872
10.6k
                    found1 || found2
873
                },
874
                _ => {
875
8.09k
                    events.push(SMBEvent::MalformedData);
876
8.09k
                    false
877
                }
878
            }
879
        },
880
        _ => {
881
            SCLogDebug!("default case: no TX");
882
12.3k
            false
883
        },
884
    };
885
178k
    if !have_tx {
886
88.2k
        let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
887
        SCLogDebug!("looking for TX {} with session_id {} tree_id {} message_id {}",
888
                &smb2_command_string(r.command),
889
                r.session_id, r.tree_id, r.message_id);
890
88.2k
        let _found = match state.get_generic_tx(2, r.command, &tx_hdr) {
891
7.16k
            Some(tx) => {
892
                SCLogDebug!("tx {} with {}/{} marked as done",
893
                        tx.id, r.command, &smb2_command_string(r.command));
894
7.16k
                if r.nt_status != SMB_NTSTATUS_PENDING {
895
6.97k
                    tx.response_done = true;
896
6.97k
                }
897
7.16k
                tx.set_status(r.nt_status, false);
898
7.16k
                tx.set_events(events);
899
7.16k
                true
900
            },
901
            _ => {
902
                SCLogDebug!("no tx found for {:?}", r);
903
81.0k
                false
904
            },
905
        };
906
89.8k
    }
907
178k
}