Coverage Report

Created: 2026-06-30 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/rust/src/nfs/nfs.rs
Line
Count
Source
1
/* Copyright (C) 2017-2021 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
// written by Victor Julien
19
20
use std;
21
use std::cmp;
22
use std::collections::HashMap;
23
use std::ffi::CString;
24
25
use nom7::{Err, Needed};
26
27
use crate::applayer;
28
use crate::applayer::*;
29
use crate::frames::*;
30
use crate::core::*;
31
use crate::conf::*;
32
use crate::filetracker::*;
33
use crate::filecontainer::*;
34
35
use crate::nfs::types::*;
36
use crate::nfs::rpc_records::*;
37
use crate::nfs::nfs_records::*;
38
use crate::nfs::nfs2_records::*;
39
use crate::nfs::nfs3_records::*;
40
41
pub static mut SURICATA_NFS_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
42
43
pub const NFS_MIN_FRAME_LEN: u16 = 32;
44
45
static mut NFS_MAX_TX: usize = 1024;
46
47
pub const RPC_TCP_PRE_CREDS: usize = 28;
48
pub const RPC_UDP_PRE_CREDS: usize = 24;
49
50
static mut ALPROTO_NFS: AppProto = ALPROTO_UNKNOWN;
51
/*
52
 * Record parsing.
53
 *
54
 * Incomplete records come in due to TCP splicing. For all record types
55
 * except READ and WRITE, processing only begins when the full record
56
 * is available. For READ/WRITE partial records are processed as well to
57
 * avoid queuing too much data.
58
 *
59
 * Getting file names.
60
 *
61
 * NFS makes heavy use of 'file handles' for operations. In many cases it
62
 * uses a file name just once and after that just the handle. For example,
63
 * if a client did a file listing (e.g. READDIRPLUS) and would READ the
64
 * file afterwards, the name will only appear in the READDIRPLUS answer.
65
 * To be able to log the names we store a mapping between file handles
66
 * and file names in NFSState::namemap.
67
 *
68
 * Mapping NFS to Suricata's transaction model.
69
 *
70
 * The easiest way to do transactions would be to map each command/reply with
71
 * the same XID to a transaction. This would allow for per XID logging, detect
72
 * etc. However this model doesn't fit well with file tracking. The file
73
 * tracking in Suricata is really expecting to be one or more files to live
74
 * inside a single transaction. Would XID pairs be a transaction however,
75
 * there would be many transactions forming a single file. This will be very
76
 * inefficient.
77
 *
78
 * The model implemented here is as follows: each file transfer is a single
79
 * transaction. All XID pairs unrelated to those file transfers create
80
 * transactions per pair.
81
 *
82
 * A complicating factor is that the procedure matching is per tx, and a
83
 * file transfer may have multiple procedures involved. Currently now only
84
 * a COMMIT after WRITEs. A vector of additional procedures is kept to
85
 * match on this.
86
 *
87
 * File tracking
88
 *
89
 * Files are tracked per 'FileTransferTracker' and are stored in the
90
 * NFSTransaction where they can be looked up per handle as part of the
91
 * Transaction lookup.
92
 */
93
94
pub static mut NFS_CFG_MAX_REQ: usize = 512;
95
pub static mut NFS_CFG_MAX_NAMES: usize = 512;
96
97
#[derive(AppLayerFrameType)]
98
pub enum NFSFrameType {
99
    RPCPdu,
100
    RPCHdr,
101
    RPCData,
102
    RPCCreds, // for rpc calls | rpc.creds [creds_flavor + creds_len + creds]
103
104
    NFSPdu,
105
    NFSStatus,
106
107
    NFS4Pdu,
108
    NFS4Hdr,
109
    NFS4Ops,
110
    NFS4Status,
111
}
112
113
#[derive(FromPrimitive, Debug, AppLayerEvent)]
114
pub enum NFSEvent {
115
    MalformedData = 0,
116
    NonExistingVersion = 1,
117
    UnsupportedVersion = 2,
118
    TooManyTransactions = 3,
119
}
120
121
#[derive(Debug)]
122
pub enum NFSTransactionTypeData {
123
    RENAME(Vec<u8>),
124
    FILE(NFSTransactionFile),
125
}
126
127
#[derive(Default, Debug)]
128
pub struct NFSTransactionFile {
129
    /// file transactions are unidirectional in the sense that they track
130
    /// a single file on one direction
131
    pub direction: Direction, // Direction::ToClient or Direction::ToServer
132
133
    /// additional procedures part of a single file transfer. Currently
134
    /// only COMMIT on WRITEs.
135
    pub file_additional_procs: Vec<u32>,
136
137
    pub chunk_count: u32,
138
139
    /// last xid of this file transfer. Last READ or COMMIT normally.
140
    pub file_last_xid: u32,
141
142
    /// after a gap, this will be set to a time in the future. If the file
143
    /// receives no updates before that, it will be considered complete.
144
    pub post_gap_ts: u64,
145
146
    /// file tracker for a single file. Boxed so that we don't use
147
    /// as much space if we're not a file tx.
148
    pub file_tracker: FileTransferTracker,
149
}
150
151
impl NFSTransactionFile {
152
48.0k
    pub fn new() -> Self {
153
48.0k
        return Self {
154
48.0k
            file_tracker: FileTransferTracker::new(),
155
48.0k
            ..Default::default()
156
48.0k
        }
157
48.0k
    }
158
318k
    pub fn update_file_flags(&mut self, flow_file_flags: u16) {
159
318k
        let dir_flag = if self.direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT };
160
318k
        self.file_tracker.file_flags = unsafe { FileFlowFlagsToFlags(flow_file_flags, dir_flag) };
161
318k
    }
162
}
163
164
#[no_mangle]
165
35.5M
pub unsafe extern "C" fn rs_nfs_gettxfiles(_state: *mut std::ffi::c_void, tx: *mut std::ffi::c_void, direction: u8) -> AppLayerGetFileState {
166
35.5M
    let tx = cast_pointer!(tx, NFSTransaction);
167
35.5M
    if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
168
35.5M
        let tx_dir : u8 = tdf.direction.into();
169
35.5M
        if direction & tx_dir != 0 {
170
35.5M
            if let Some(sfcm) = { SURICATA_NFS_FILE_CONFIG } {
171
35.5M
                return AppLayerGetFileState { fc: &mut tdf.file_tracker.file, cfg: sfcm.files_sbcfg }
172
0
            }
173
10.7k
        }
174
5.28k
    }
175
16.0k
    AppLayerGetFileState::err()
176
35.5M
}
177
178
#[derive(Debug)]
179
pub struct NFSTransaction {
180
    pub id: u64,    /// internal id
181
    pub xid: u32,   /// nfs req/reply pair id
182
    pub procedure: u32,
183
    /// file name of the object we're dealing with. In case of RENAME
184
    /// this is the 'from' or original name.
185
    pub file_name: Vec<u8>,
186
187
    pub auth_type: u32,
188
    pub request_machine_name: Vec<u8>,
189
    pub request_uid: u32,
190
    pub request_gid: u32,
191
192
    pub rpc_response_status: u32,
193
    pub nfs_response_status: u32,
194
195
    pub is_first: bool,
196
    pub is_last: bool,
197
198
    /// for state tracking. false means this side is in progress, true
199
    /// that it's complete.
200
    pub request_done: bool,
201
    pub response_done: bool,
202
203
    pub nfs_version: u16,
204
205
    /// is a special file tx that we look up by file_handle instead of XID
206
    pub is_file_tx: bool,
207
    pub is_file_closed: bool,
208
    pub file_handle: Vec<u8>,
209
210
    /// Procedure type specific data
211
    /// TODO see if this can be an `Option<Box<NFSTransactionTypeData>>`. Initial
212
    /// attempt failed.
213
    pub type_data: Option<NFSTransactionTypeData>,
214
215
    pub tx_data: AppLayerTxData,
216
}
217
218
impl Default for NFSTransaction {
219
0
    fn default() -> Self {
220
0
        Self::new()
221
0
    }
222
}
223
224
impl NFSTransaction {
225
327k
    pub fn new() -> Self {
226
327k
        return Self {
227
327k
            id: 0,
228
327k
            xid: 0,
229
327k
            procedure: 0,
230
327k
            file_name:Vec::new(),
231
327k
            request_machine_name:Vec::new(),
232
327k
            request_uid:0,
233
327k
            request_gid:0,
234
327k
            rpc_response_status:0,
235
327k
            nfs_response_status:0,
236
327k
            auth_type: 0,
237
327k
            is_first: false,
238
327k
            is_last: false,
239
327k
            request_done: false,
240
327k
            response_done: false,
241
327k
            nfs_version:0,
242
327k
            is_file_tx: false,
243
327k
            is_file_closed: false,
244
327k
            file_handle:Vec::new(),
245
327k
            type_data: None,
246
327k
            tx_data: AppLayerTxData::new(),
247
327k
        }
248
327k
    }
249
250
327k
    pub fn free(&mut self) {
251
327k
        debug_validate_bug_on!(self.tx_data.files_opened > 1);
252
327k
        debug_validate_bug_on!(self.tx_data.files_logged > 1);
253
327k
    }
254
}
255
256
impl Transaction for NFSTransaction {
257
1.09G
    fn id(&self) -> u64 {
258
1.09G
        self.id
259
1.09G
    }
260
}
261
262
impl Drop for NFSTransaction {
263
327k
    fn drop(&mut self) {
264
68.0k
        if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = self.type_data {
265
48.0k
            if let Some(sfcm) = unsafe { SURICATA_NFS_FILE_CONFIG } {
266
48.0k
                tdf.file_tracker.file.free(sfcm);
267
48.0k
            }
268
279k
        }
269
327k
        self.free();
270
327k
    }
271
}
272
273
#[derive(Debug)]
274
pub struct NFSRequestXidMap {
275
    pub progver: u32,
276
    pub procedure: u32,
277
    pub chunk_offset: u64,
278
    pub file_name:Vec<u8>,
279
280
    /// READ replies can use this to get to the handle the request used
281
    pub file_handle:Vec<u8>,
282
283
    pub gssapi_proc: u32,
284
    pub gssapi_service: u32,
285
}
286
287
impl NFSRequestXidMap {
288
733k
    pub fn new(progver: u32, procedure: u32, chunk_offset: u64) -> NFSRequestXidMap {
289
733k
        NFSRequestXidMap {
290
733k
            progver,
291
733k
            procedure,
292
733k
            chunk_offset,
293
733k
            file_name:Vec::new(),
294
733k
            file_handle:Vec::new(),
295
733k
            gssapi_proc: 0,
296
733k
            gssapi_service: 0,
297
733k
        }
298
733k
    }
299
}
300
301
/// little wrapper around the FileTransferTracker::new_chunk method
302
166k
pub fn filetracker_newchunk(ft: &mut FileTransferTracker, name: &[u8], data: &[u8],
303
166k
        chunk_offset: u64, chunk_size: u32, fill_bytes: u8, is_last: bool, xid: &u32)
304
{
305
166k
    if let Some(sfcm) = unsafe { SURICATA_NFS_FILE_CONFIG } {
306
166k
        ft.new_chunk(sfcm, name, data, chunk_offset,
307
166k
                chunk_size, fill_bytes, is_last, xid);
308
166k
    }
309
166k
}
310
311
0
fn filetracker_trunc(ft: &mut FileTransferTracker)
312
{
313
0
    if let Some(sfcm) = unsafe { SURICATA_NFS_FILE_CONFIG } {
314
0
        ft.trunc(sfcm);
315
0
    }
316
0
}
317
318
3.07k
pub fn filetracker_close(ft: &mut FileTransferTracker)
319
{
320
3.07k
    if let Some(sfcm) = unsafe { SURICATA_NFS_FILE_CONFIG } {
321
3.07k
        ft.close(sfcm);
322
3.07k
    }
323
3.07k
}
324
325
116k
fn filetracker_update(ft: &mut FileTransferTracker, data: &[u8], gap_size: u32) -> u32
326
{
327
116k
    if let Some(sfcm) = unsafe { SURICATA_NFS_FILE_CONFIG } {
328
116k
        ft.update(sfcm, data, gap_size)
329
    } else {
330
0
        0
331
    }
332
116k
}
333
334
335
#[derive(Debug)]
336
pub struct NFSState {
337
    state_data: AppLayerStateData,
338
339
    /// map xid to procedure so replies can lookup the procedure
340
    pub requestmap: HashMap<u32, NFSRequestXidMap>,
341
342
    /// map file handle (1) to name (2)
343
    pub namemap: HashMap<Vec<u8>, Vec<u8>>,
344
345
    /// transactions list
346
    pub transactions: Vec<NFSTransaction>,
347
348
    /// partial record tracking
349
    pub ts_chunk_xid: u32,
350
    pub tc_chunk_xid: u32,
351
    /// size of the current chunk that we still need to receive
352
    pub ts_chunk_left: u32,
353
    pub tc_chunk_left: u32,
354
    /// file handle of in progress toserver WRITE file chunk
355
    ts_chunk_fh: Vec<u8>,
356
357
    ts_ssn_gap: bool,
358
    tc_ssn_gap: bool,
359
360
    ts_gap: bool, // last TS update was gap
361
    tc_gap: bool, // last TC update was gap
362
363
    is_udp: bool,
364
365
    /// true as long as we have file txs that are in a post-gap
366
    /// state. It means we'll do extra house keeping for those.
367
    check_post_gap_file_txs: bool,
368
    post_gap_files_checked: bool,
369
370
    pub nfs_version: u16,
371
372
    /// tx counter for assigning incrementing id's to tx's
373
    tx_id: u64,
374
375
    /// Timestamp in seconds of last update. This is packet time,
376
    /// potentially coming from pcaps.
377
    ts: u64,
378
}
379
380
impl Default for NFSState {
381
0
    fn default() -> Self {
382
0
        Self::new()
383
0
    }
384
}
385
386
impl State<NFSTransaction> for NFSState {
387
367M
    fn get_transaction_count(&self) -> usize {
388
367M
        self.transactions.len()
389
367M
    }
390
391
731M
    fn get_transaction_by_index(&self, index: usize) -> Option<&NFSTransaction> {
392
731M
        self.transactions.get(index)
393
731M
    }
394
}
395
396
impl NFSState {
397
    /// Allocation function for a new TLS parser instance
398
22.3k
    pub fn new() -> NFSState {
399
22.3k
        NFSState {
400
22.3k
            state_data: AppLayerStateData::new(),
401
22.3k
            requestmap:HashMap::new(),
402
22.3k
            namemap:HashMap::new(),
403
22.3k
            transactions: Vec::new(),
404
22.3k
            ts_chunk_xid:0,
405
22.3k
            tc_chunk_xid:0,
406
22.3k
            ts_chunk_left:0,
407
22.3k
            tc_chunk_left:0,
408
22.3k
            ts_chunk_fh:Vec::new(),
409
22.3k
            ts_ssn_gap:false,
410
22.3k
            tc_ssn_gap:false,
411
22.3k
            ts_gap:false,
412
22.3k
            tc_gap:false,
413
22.3k
            is_udp:false,
414
22.3k
            check_post_gap_file_txs:false,
415
22.3k
            post_gap_files_checked:false,
416
22.3k
            nfs_version:0,
417
22.3k
            tx_id:0,
418
22.3k
            ts: 0,
419
22.3k
        }
420
22.3k
    }
421
422
1.24M
    fn update_ts(&mut self, ts: u64) {
423
1.24M
        if ts != self.ts {
424
443
            self.ts = ts;
425
443
            self.post_gap_files_checked = false;
426
1.24M
        }
427
1.24M
    }
428
429
327k
    pub fn new_tx(&mut self) -> NFSTransaction {
430
327k
        let mut tx = NFSTransaction::new();
431
327k
        self.tx_id += 1;
432
327k
        tx.id = self.tx_id;
433
327k
        if self.transactions.len() > unsafe { NFS_MAX_TX } {
434
            // set at least one another transaction to the drop state
435
71.1k
            for tx_old in &mut self.transactions {
436
71.1k
                if !tx_old.request_done || !tx_old.response_done {
437
70.9k
                    tx_old.tx_data.updated_tc = true;
438
70.9k
                    tx_old.tx_data.updated_ts = true;
439
70.9k
                    tx_old.request_done = true;
440
70.9k
                    tx_old.response_done = true;
441
70.9k
                    tx_old.is_file_closed = true;
442
70.9k
                    tx_old.tx_data.set_event(NFSEvent::TooManyTransactions as u8);
443
70.9k
                    break;
444
201
                }
445
            }
446
256k
        }
447
327k
        return tx;
448
327k
    }
449
450
141k
    pub fn free_tx(&mut self, tx_id: u64) {
451
        //SCLogNotice!("Freeing TX with ID {}", tx_id);
452
141k
        let len = self.transactions.len();
453
141k
        let mut found = false;
454
141k
        let mut index = 0;
455
2.06M
        for i in 0..len {
456
2.06M
            let tx = &self.transactions[i];
457
2.06M
            if tx.id == tx_id + 1 {
458
141k
                found = true;
459
141k
                index = i;
460
141k
                break;
461
1.92M
            }
462
        }
463
141k
        if found {
464
141k
            SCLogDebug!("freeing TX with ID {} at index {}", tx_id, index);
465
141k
            self.transactions.remove(index);
466
141k
        }
467
141k
    }
468
469
16.2k
    pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&NFSTransaction> {
470
62.5k
        return self.transactions.iter().find(|&tx| tx.id == tx_id + 1);
471
16.2k
    }
472
473
84.4k
    pub fn get_tx_by_xid(&mut self, tx_xid: u32) -> Option<&mut NFSTransaction> {
474
2.26M
        return self.transactions.iter_mut().find(|tx| !tx.is_file_tx && tx.xid == tx_xid);
475
84.4k
    }
476
477
    /// Set an event. The event is set on the most recent transaction.
478
624k
    pub fn set_event(&mut self, event: NFSEvent) {
479
624k
        let len = self.transactions.len();
480
624k
        if len == 0 {
481
289k
            return;
482
334k
        }
483
484
334k
        let tx = &mut self.transactions[len - 1];
485
334k
        tx.tx_data.set_event(event as u8);
486
624k
    }
487
488
    // TODO maybe not enough users to justify a func
489
84.4k
    pub fn mark_response_tx_done(&mut self, xid: u32, rpc_status: u32, nfs_status: u32, resp_handle: &[u8])
490
    {
491
84.4k
        if let Some(mytx) = self.get_tx_by_xid(xid) {
492
66.6k
            mytx.tx_data.updated_tc = true;
493
66.6k
            mytx.tx_data.updated_ts = true;
494
66.6k
            mytx.response_done = true;
495
66.6k
            mytx.rpc_response_status = rpc_status;
496
66.6k
            mytx.nfs_response_status = nfs_status;
497
66.6k
            if mytx.file_handle.is_empty() && !resp_handle.is_empty() {
498
7.14k
                mytx.file_handle = resp_handle.to_vec();
499
59.5k
            }
500
            
501
            SCLogDebug!("process_reply_record: tx ID {} XID {:04X} REQUEST {} RESPONSE {}",
502
                        mytx.id, mytx.xid, mytx.request_done, mytx.response_done);
503
17.7k
        } else {
504
17.7k
            //SCLogNotice!("process_reply_record: not TX found for XID {}", r.hdr.xid);
505
17.7k
        }
506
84.4k
    }
507
508
988k
    fn add_rpc_udp_ts_pdu(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], rpc_len: i64) -> Option<Frame> {
509
988k
        let rpc_udp_ts_pdu = Frame::new(flow, stream_slice, input, rpc_len, NFSFrameType::RPCPdu as u8);
510
        SCLogDebug!("rpc_udp_pdu ts frame {:?}", rpc_udp_ts_pdu);
511
988k
        rpc_udp_ts_pdu
512
988k
    }
513
514
470k
    fn add_rpc_udp_ts_creds(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], creds_len: i64) {
515
470k
        let _rpc_udp_ts_creds = Frame::new(flow, stream_slice, input, creds_len, NFSFrameType::RPCCreds as u8);
516
        SCLogDebug!("rpc_creds ts frame {:?}", _rpc_udp_ts_creds);
517
470k
    }
518
519
900k
    fn add_rpc_tcp_ts_pdu(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], rpc_len: i64) -> Option<Frame> {
520
900k
        let rpc_tcp_ts_pdu = Frame::new(flow, stream_slice, input, rpc_len, NFSFrameType::RPCPdu as u8);
521
        SCLogDebug!("rpc_tcp_pdu ts frame {:?}", rpc_tcp_ts_pdu);
522
900k
        rpc_tcp_ts_pdu
523
900k
    }
524
525
241k
    fn add_rpc_tcp_ts_creds(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], creds_len: i64) {
526
241k
        let _rpc_tcp_ts_creds = Frame::new(flow, stream_slice, input, creds_len, NFSFrameType::RPCCreds as u8);
527
        SCLogDebug!("rpc_tcp_ts_creds {:?}", _rpc_tcp_ts_creds);
528
241k
    }
529
530
440k
    fn add_nfs_ts_frame(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nfs_len: i64) {
531
440k
        let _nfs_req_pdu = Frame::new(flow, stream_slice, input, nfs_len, NFSFrameType::NFSPdu as u8);
532
        SCLogDebug!("nfs_ts_pdu Frame {:?}", _nfs_req_pdu);
533
440k
    }
534
535
221k
    fn add_nfs4_ts_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nfs4_len: i64) {
536
221k
        let _nfs4_ts_pdu = Frame::new(flow, stream_slice, input, nfs4_len, NFSFrameType::NFS4Pdu as u8);
537
        SCLogDebug!("nfs4_ts_pdu Frame: {:?}", _nfs4_ts_pdu);
538
221k
        if nfs4_len > 8 {
539
202k
            let _nfs4_ts_hdr = Frame::new(flow, stream_slice, input, 8, NFSFrameType::NFS4Hdr as u8);
540
202k
            SCLogDebug!("nfs4_ts_hdr Frame {:?}", _nfs4_ts_hdr);
541
202k
            let _nfs4_ts_ops = Frame::new(flow, stream_slice, &input[8..], nfs4_len - 8, NFSFrameType::NFS4Ops as u8);
542
202k
            SCLogDebug!("nfs4_ts_ops Frame {:?}", _nfs4_ts_ops);
543
202k
        }
544
221k
    }
545
546
813k
    fn add_rpc_udp_tc_pdu(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], rpc_len: i64) -> Option<Frame> {
547
813k
        let rpc_udp_tc_pdu = Frame::new(flow, stream_slice, input, rpc_len, NFSFrameType::RPCPdu as u8);
548
        SCLogDebug!("rpc_tc_pdu frame {:?}", rpc_udp_tc_pdu);
549
813k
        rpc_udp_tc_pdu
550
813k
    }
551
552
129k
    fn add_rpc_udp_tc_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], rpc_len: i64) {
553
129k
        if rpc_len > 8 {
554
129k
            let _rpc_udp_tc_hdr = Frame::new(flow, stream_slice, input, 8, NFSFrameType::RPCHdr as u8);
555
129k
            let _rpc_udp_tc_data = Frame::new(flow, stream_slice, &input[8..], rpc_len - 8, NFSFrameType::RPCData as u8);
556
129k
            SCLogDebug!("rpc_udp_tc_hdr frame {:?}", _rpc_udp_tc_hdr);
557
129k
            SCLogDebug!("rpc_udp_tc_data frame {:?}", _rpc_udp_tc_data);
558
129k
        }
559
129k
    }
560
561
666k
    fn add_rpc_tcp_tc_pdu(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], rpc_tcp_len: i64) -> Option<Frame> {
562
666k
        let rpc_tcp_tc_pdu = Frame::new(flow, stream_slice, input, rpc_tcp_len, NFSFrameType::RPCPdu as u8);
563
        SCLogDebug!("rpc_tcp_pdu tc frame {:?}", rpc_tcp_tc_pdu);
564
666k
        rpc_tcp_tc_pdu
565
666k
    }
566
567
85.8k
    fn add_rpc_tcp_tc_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], rpc_tcp_len: i64) {
568
85.8k
        if rpc_tcp_len > 12 {
569
85.8k
            let _rpc_tcp_tc_hdr = Frame::new(flow, stream_slice, input, 12, NFSFrameType::RPCHdr as u8);
570
85.8k
            let _rpc_tcp_tc_data = Frame::new(flow, stream_slice, &input[12..], rpc_tcp_len - 12, NFSFrameType::RPCData as u8);
571
85.8k
            SCLogDebug!("rpc_tcp_tc_hdr frame {:?}", _rpc_tcp_tc_hdr);
572
85.8k
            SCLogDebug!("rpc_tcp_tc_data frame {:?}", _rpc_tcp_tc_data);
573
85.8k
        }
574
85.8k
    }
575
576
94.4k
    fn add_nfs_tc_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nfs_len: i64) {
577
94.4k
        if nfs_len > 0 {
578
84.8k
            let _nfs_tc_pdu = Frame::new(flow, stream_slice, input, nfs_len, NFSFrameType::NFSPdu as u8);
579
84.8k
            SCLogDebug!("nfs_tc_pdu frame {:?}", _nfs_tc_pdu);
580
84.8k
            let _nfs_res_status = Frame::new(flow, stream_slice, input, 4, NFSFrameType::NFSStatus as u8);
581
84.8k
            SCLogDebug!("nfs_tc_status frame {:?}", _nfs_res_status);
582
84.8k
        }
583
94.4k
    }
584
585
72.9k
    fn add_nfs4_tc_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nfs4_len: i64) {
586
72.9k
        if nfs4_len > 0 {
587
72.5k
            let _nfs4_tc_pdu = Frame::new(flow, stream_slice, input, nfs4_len, NFSFrameType::NFS4Pdu as u8);
588
72.5k
            SCLogDebug!("nfs4_tc_pdu frame {:?}", _nfs4_tc_pdu);
589
72.5k
            let _nfs4_tc_status = Frame::new(flow, stream_slice, input, 4, NFSFrameType::NFS4Status as u8);
590
72.5k
            SCLogDebug!("nfs4_tc_status frame {:?}", _nfs4_tc_status);
591
72.5k
        }
592
72.9k
        if nfs4_len > 8 {
593
71.5k
            let _nfs4_tc_hdr = Frame::new(flow, stream_slice, input, 8, NFSFrameType::NFS4Hdr as u8);
594
71.5k
            SCLogDebug!("nfs4_tc_hdr frame {:?}", _nfs4_tc_hdr);
595
71.5k
            let _nfs4_tc_ops = Frame::new(flow, stream_slice, &input[8..], nfs4_len - 8, NFSFrameType::NFS4Ops as u8);
596
71.5k
            SCLogDebug!("nfs4_tc_ops frame {:?}", _nfs4_tc_ops);
597
71.5k
        }
598
72.9k
    }
599
600
0
    fn post_gap_housekeeping_for_files(&mut self)
601
    {
602
0
        let mut post_gap_txs = false;
603
0
        for tx in &mut self.transactions {
604
0
            if let Some(NFSTransactionTypeData::FILE(ref mut f)) = tx.type_data {
605
0
                if f.post_gap_ts > 0 {
606
0
                    if self.ts > f.post_gap_ts {
607
0
                        tx.request_done = true;
608
0
                        tx.response_done = true;
609
0
                        filetracker_trunc(&mut f.file_tracker);
610
0
                    } else {
611
0
                        post_gap_txs = true;
612
0
                    }
613
0
                }
614
0
            }
615
        }
616
0
        self.check_post_gap_file_txs = post_gap_txs;
617
0
    }
618
619
    /* after a gap we will consider all transactions complete for our
620
     * direction. File transfer transactions are an exception. Those
621
     * can handle gaps. For the file transactions we set the current
622
     * (flow) time and prune them in 60 seconds if no update for them
623
     * was received. */
624
65.8k
    fn post_gap_housekeeping(&mut self, dir: Direction)
625
    {
626
65.8k
        if self.ts_ssn_gap && dir == Direction::ToServer {
627
166
            for tx in &mut self.transactions {
628
22
                if tx.id >= self.tx_id {
629
                    SCLogDebug!("post_gap_housekeeping: done");
630
22
                    break;
631
0
                }
632
0
                if let Some(NFSTransactionTypeData::FILE(ref mut f)) = tx.type_data {
633
                    // leaving FILE txs open as they can deal with gaps. We
634
                    // remove them after 60 seconds of no activity though.
635
0
                    if f.post_gap_ts == 0 {
636
0
                        f.post_gap_ts = self.ts + 60;
637
0
                        self.check_post_gap_file_txs = true;
638
0
                    }
639
0
                } else {
640
0
                    SCLogDebug!("post_gap_housekeeping: tx {} marked as done TS", tx.id);
641
0
                    tx.request_done = true;
642
0
                }
643
            }
644
65.6k
        } else if self.tc_ssn_gap && dir == Direction::ToClient {
645
198
            for tx in &mut self.transactions {
646
13
                if tx.id >= self.tx_id {
647
                    SCLogDebug!("post_gap_housekeeping: done");
648
13
                    break;
649
0
                }
650
0
                if let Some(NFSTransactionTypeData::FILE(ref mut f)) = tx.type_data {
651
                    // leaving FILE txs open as they can deal with gaps. We
652
                    // remove them after 60 seconds of no activity though.
653
0
                    if f.post_gap_ts == 0 {
654
0
                        f.post_gap_ts = self.ts + 60;
655
0
                        self.check_post_gap_file_txs = true;
656
0
                    }
657
0
                } else {
658
0
                    SCLogDebug!("post_gap_housekeeping: tx {} marked as done TC", tx.id);
659
0
                    tx.request_done = true;
660
0
                    tx.response_done = true;
661
0
                }
662
            }
663
65.4k
        }
664
65.8k
    }
665
666
18.3k
    pub fn process_request_record_lookup(&mut self, r: &RpcPacket, xidmap: &mut NFSRequestXidMap) {
667
18.3k
        match parse_nfs3_request_lookup(r.prog_data) {
668
6.39k
            Ok((_, lookup)) => {
669
6.39k
                SCLogDebug!("LOOKUP {:?}", lookup);
670
6.39k
                xidmap.file_name = lookup.name_vec;
671
6.39k
            },
672
11.9k
            _ => {
673
11.9k
                self.set_event(NFSEvent::MalformedData);
674
11.9k
            },
675
        };
676
18.3k
    }
677
678
38.8k
    pub fn xidmap_handle2name(&mut self, xidmap: &mut NFSRequestXidMap) {
679
38.8k
        if let Some(n) = self.namemap.get(&xidmap.file_handle) {
680
3.72k
            SCLogDebug!("xidmap_handle2name: name {:?}", n);
681
3.72k
            xidmap.file_name = n.to_vec();
682
35.1k
        } else {
683
35.1k
            SCLogDebug!("xidmap_handle2name: object {:?} not found", xidmap.file_handle);
684
35.1k
        }
685
38.8k
    }
686
687
    /// complete request record
688
642k
    fn process_request_record(&mut self, flow: *const Flow, stream_slice: &StreamSlice, r: &RpcPacket) {
689
        SCLogDebug!("REQUEST {} procedure {} ({}) blob size {}",
690
                r.hdr.xid, r.procedure, self.requestmap.len(), r.prog_data.len());
691
692
642k
        match r.progver {
693
            4 => {
694
221k
                self.add_nfs4_ts_frames(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
695
221k
                self.process_request_record_v4(r)
696
            },
697
            3 => {
698
402k
                self.add_nfs_ts_frame(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
699
402k
                self.process_request_record_v3(r)
700
            },
701
            2 => {
702
603
                self.add_nfs_ts_frame(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
703
603
                self.process_request_record_v2(r)
704
            },
705
17.6k
            _ => { },
706
        }
707
642k
    }
708
709
48.0k
    pub fn new_file_tx(&mut self, file_handle: &[u8], file_name: &[u8], direction: Direction)
710
48.0k
        -> &mut NFSTransaction
711
    {
712
48.0k
        let mut tx = self.new_tx();
713
48.0k
        tx.file_name = file_name.to_vec();
714
48.0k
        tx.file_handle = file_handle.to_vec();
715
48.0k
        tx.is_file_tx = true;
716
717
48.0k
        tx.type_data = Some(NFSTransactionTypeData::FILE(NFSTransactionFile::new()));
718
48.0k
        if let Some(NFSTransactionTypeData::FILE(ref mut d)) = tx.type_data {
719
48.0k
            d.direction = direction;
720
48.0k
            d.file_tracker.tx_id = tx.id - 1;
721
48.0k
            tx.tx_data.update_file_flags(self.state_data.file_flags);
722
48.0k
            d.update_file_flags(tx.tx_data.file_flags);
723
48.0k
        }
724
48.0k
        tx.tx_data.init_files_opened();
725
48.0k
        tx.tx_data.file_tx = if direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; // TODO direction to flag func?
726
        SCLogDebug!("new_file_tx: TX FILE created: ID {} NAME {}",
727
                tx.id, String::from_utf8_lossy(file_name));
728
48.0k
        self.transactions.push(tx);
729
48.0k
        let tx_ref = self.transactions.last_mut();
730
48.0k
        return tx_ref.unwrap();
731
48.0k
    }
732
733
384k
    pub fn get_file_tx_by_handle(&mut self, file_handle: &[u8], direction: Direction)
734
384k
        -> Option<&mut NFSTransaction>
735
    {
736
384k
        let fh = file_handle.to_vec();
737
42.6M
        for tx in &mut self.transactions {
738
33.8M
            if let Some(NFSTransactionTypeData::FILE(ref mut d)) = tx.type_data {
739
33.5M
                if tx.is_file_tx && !tx.is_file_closed &&
740
577k
                    direction == d.direction &&
741
523k
                        tx.file_handle == fh
742
                {
743
270k
                    tx.tx_data.update_file_flags(self.state_data.file_flags);
744
270k
                    d.update_file_flags(tx.tx_data.file_flags);
745
                    SCLogDebug!("Found NFS file TX with ID {} XID {:04X}", tx.id, tx.xid);
746
270k
                    tx.tx_data.updated_tc = true;
747
270k
                    tx.tx_data.updated_ts = true;
748
270k
                    return Some(tx);
749
33.2M
                }
750
8.99M
            }
751
        }
752
        SCLogDebug!("Failed to find NFS TX with handle {:?}", file_handle);
753
114k
        return None;
754
384k
    }
755
756
159k
    pub fn process_write_record<'b>(&mut self, r: &RpcPacket<'b>, w: &Nfs3RequestWrite<'b>) -> u32 {
757
159k
        let mut fill_bytes = 0;
758
159k
        let pad = w.count % 4;
759
159k
        if pad != 0 {
760
12.7k
            fill_bytes = 4 - pad;
761
147k
        }
762
763
        // linux defines a max of 1mb. Allow several multiples.
764
159k
        if w.count == 0 || w.count > 16777216 {
765
6.66k
            return 0;
766
153k
        }
767
768
        // for now assume that stable FILE_SYNC flags means a single chunk
769
153k
        let is_last = w.stable == 2;
770
153k
        let file_handle = w.handle.value.to_vec();
771
153k
        let file_name = if let Some(name) = self.namemap.get(w.handle.value) {
772
            SCLogDebug!("WRITE name {:?}", name);
773
3.42k
            name.to_vec()
774
        } else {
775
            SCLogDebug!("WRITE object {:?} not found", w.handle.value);
776
149k
            Vec::new()
777
        };
778
779
153k
        let found = match self.get_file_tx_by_handle(&file_handle, Direction::ToServer) {
780
114k
            Some(tx) => {
781
114k
                if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
782
114k
                    filetracker_newchunk(&mut tdf.file_tracker,
783
114k
                            &file_name, w.file_data, w.offset,
784
114k
                            w.file_len, fill_bytes as u8, is_last, &r.hdr.xid);
785
114k
                    tdf.chunk_count += 1;
786
114k
                    if is_last {
787
9.22k
                        tdf.file_last_xid = r.hdr.xid;
788
9.22k
                        tx.is_last = true;
789
9.22k
                        tx.response_done = true;
790
9.22k
                        tx.is_file_closed = true;
791
104k
                    }
792
114k
                    true
793
                } else {
794
0
                    false
795
                }
796
            },
797
38.9k
            None => { false },
798
        };
799
153k
        if !found {
800
38.9k
            let tx = self.new_file_tx(&file_handle, &file_name, Direction::ToServer);
801
38.9k
            if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
802
38.9k
                filetracker_newchunk(&mut tdf.file_tracker,
803
38.9k
                        &file_name, w.file_data, w.offset,
804
38.9k
                        w.file_len, fill_bytes as u8, is_last, &r.hdr.xid);
805
38.9k
                tx.procedure = NFSPROC3_WRITE;
806
38.9k
                tx.xid = r.hdr.xid;
807
38.9k
                tx.is_first = true;
808
38.9k
                tx.nfs_version = r.progver as u16;
809
38.9k
                if is_last {
810
24.3k
                    tdf.file_last_xid = r.hdr.xid;
811
24.3k
                    tx.is_last = true;
812
24.3k
                    tx.request_done = true;
813
24.3k
                    tx.is_file_closed = true;
814
24.3k
                }
815
0
            }
816
114k
        }
817
153k
        if !self.is_udp {
818
67.1k
            self.ts_chunk_xid = r.hdr.xid;
819
67.1k
            debug_validate_bug_on!(w.file_data.len() as u32 > w.count);
820
67.1k
            self.ts_chunk_left = w.count - w.file_data.len() as u32;
821
67.1k
            self.ts_chunk_fh = file_handle;
822
            SCLogDebug!("REQUEST chunk_xid {:04X} chunk_left {}", self.ts_chunk_xid, self.ts_chunk_left);
823
86.0k
        }
824
153k
        0
825
159k
    }
826
827
71.4k
    fn process_partial_write_request_record<'b>(&mut self, r: &RpcPacket<'b>, w: &Nfs3RequestWrite<'b>) -> u32 {
828
        SCLogDebug!("REQUEST {} procedure {} blob size {}", r.hdr.xid, r.procedure, r.prog_data.len());
829
830
71.4k
        let mut xidmap = NFSRequestXidMap::new(r.progver, r.procedure, 0);
831
71.4k
        xidmap.file_handle = w.handle.value.to_vec();
832
71.4k
        if self.requestmap.len() < unsafe { NFS_CFG_MAX_REQ } {
833
71.4k
            self.requestmap.insert(r.hdr.xid, xidmap);
834
71.4k
        }
835
836
71.4k
        return self.process_write_record(r, w);
837
71.4k
    }
838
839
215k
    fn process_reply_record(&mut self, flow: *const Flow, stream_slice: &StreamSlice, r: &RpcReplyPacket) -> u32 {
840
        let mut xidmap;
841
215k
        match self.requestmap.remove(&r.hdr.xid) {
842
169k
            Some(p) => { xidmap = p; },
843
            _ => {
844
                SCLogDebug!("REPLY: xid {:04X} NOT FOUND. GAPS? TS:{} TC:{}",
845
                        r.hdr.xid, self.ts_ssn_gap, self.tc_ssn_gap);
846
847
                // TODO we might be able to try to infer from the size + data
848
                // that this is a READ reply and pass the data to the file API anyway?
849
45.6k
                return 0;
850
            },
851
        }
852
        SCLogDebug!("process_reply_record: removed xid {:04X} from requestmap",
853
            r.hdr.xid);
854
855
169k
        if self.nfs_version == 0 {
856
5.45k
            self.nfs_version = xidmap.progver as u16;
857
163k
        }
858
859
169k
        match xidmap.progver {
860
            2 => {
861
                SCLogDebug!("NFSv2 reply record");
862
17.4k
                self.add_nfs_tc_frames(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
863
17.4k
                self.process_reply_record_v2(r, &xidmap);
864
17.4k
                return 0;
865
            },
866
            3 => {
867
                SCLogDebug!("NFSv3 reply record");
868
76.9k
                self.add_nfs_tc_frames(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
869
76.9k
                self.process_reply_record_v3(r, &mut xidmap);
870
76.9k
                return 0;
871
            },
872
            4 => {
873
                SCLogDebug!("NFSv4 reply record");
874
72.9k
                self.add_nfs4_tc_frames(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
875
72.9k
                self.process_reply_record_v4(r, &mut xidmap);
876
72.9k
                return 0;
877
            },
878
            _ => {
879
                SCLogDebug!("Invalid NFS version");
880
2.08k
                self.set_event(NFSEvent::NonExistingVersion);
881
2.08k
                return 0;
882
            },
883
        }
884
215k
    }
885
886
    // update in progress chunks for file transfers
887
    // return how much data we consumed
888
1.24M
    fn filetracker_update(&mut self, direction: Direction, data: &[u8], gap_size: u32) -> u32 {
889
1.24M
        let mut chunk_left = if direction == Direction::ToServer {
890
651k
            self.ts_chunk_left
891
        } else {
892
598k
            self.tc_chunk_left
893
        };
894
1.24M
        if chunk_left == 0 {
895
1.06M
            return 0
896
181k
        }
897
181k
        let xid = if direction == Direction::ToServer {
898
139k
            self.ts_chunk_xid
899
        } else {
900
42.0k
            self.tc_chunk_xid
901
        };
902
        SCLogDebug!("filetracker_update: chunk left {}, input {} chunk_xid {:04X}", chunk_left, data.len(), xid);
903
904
        let file_handle;
905
        // we have the data that we expect
906
181k
        if chunk_left <= data.len() as u32 {
907
31.5k
            chunk_left = 0;
908
909
31.5k
            if direction == Direction::ToServer {
910
30.5k
                self.ts_chunk_xid = 0;
911
30.5k
                file_handle = self.ts_chunk_fh.to_vec();
912
30.5k
                self.ts_chunk_fh.clear();
913
30.5k
            } else {
914
983
                self.tc_chunk_xid = 0;
915
916
                // chunk done, remove requestmap entry
917
983
                match self.requestmap.remove(&xid) {
918
                    None => {
919
                        SCLogDebug!("no file handle found for XID {:04X}", xid);
920
195
                        return 0
921
                    },
922
788
                    Some(xidmap) => {
923
788
                        file_handle = xidmap.file_handle.to_vec();
924
788
                    },
925
                }
926
            }
927
        } else {
928
149k
            chunk_left -= data.len() as u32;
929
930
149k
            if direction == Direction::ToServer {
931
108k
                file_handle = self.ts_chunk_fh.to_vec();
932
108k
            } else {
933
                // see if we have a file handle to work on
934
41.0k
                match self.requestmap.get(&xid) {
935
                    None => {
936
                        SCLogDebug!("no file handle found for XID {:04X}", xid);
937
687
                        return 0
938
                    },
939
40.3k
                    Some(xidmap) => {
940
40.3k
                        file_handle = xidmap.file_handle.to_vec();
941
40.3k
                    },
942
                }
943
            }
944
        }
945
946
180k
        if direction == Direction::ToServer {
947
139k
            self.ts_chunk_left = chunk_left;
948
139k
        } else {
949
41.1k
            self.tc_chunk_left = chunk_left;
950
41.1k
        }
951
952
180k
        let ssn_gap = self.ts_ssn_gap | self.tc_ssn_gap;
953
        // get the tx and update it
954
180k
        let consumed = match self.get_file_tx_by_handle(&file_handle, direction) {
955
116k
            Some(tx) => {
956
116k
                if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
957
116k
                    if ssn_gap {
958
0
                        let queued_data = tdf.file_tracker.get_queued_size();
959
0
                        if queued_data > 2000000 { // TODO should probably be configurable
960
0
                            SCLogDebug!("QUEUED size {} while we've seen GAPs. Truncating file.", queued_data);
961
0
                            filetracker_trunc(&mut tdf.file_tracker);
962
0
                        }
963
116k
                    }
964
965
                    // reset timestamp if we get called after a gap
966
116k
                    if tdf.post_gap_ts > 0 {
967
0
                        tdf.post_gap_ts = 0;
968
116k
                    }
969
970
116k
                    tdf.chunk_count += 1;
971
116k
                    let cs = filetracker_update(&mut tdf.file_tracker, data, gap_size);
972
                    /* see if we need to close the tx */
973
116k
                    if tdf.file_tracker.is_done() {
974
1.59k
                        if direction == Direction::ToClient {
975
961
                            tx.response_done = true;
976
961
                            tx.is_file_closed = true;
977
961
                            SCLogDebug!("TX {} response is done now that the file track is ready", tx.id);
978
961
                        } else {
979
637
                            tx.request_done = true;
980
637
                            tx.is_file_closed = true;
981
637
                            SCLogDebug!("TX {} request is done now that the file track is ready", tx.id);
982
637
                        }
983
115k
                    }
984
116k
                    cs
985
                } else {
986
0
                    0
987
                }
988
            },
989
63.3k
            None => { 0 },
990
        };
991
180k
        return consumed;
992
1.24M
    }
993
994
    /// xidmapr is an Option as it's already removed from the map if we
995
    /// have a complete record. Otherwise we do a lookup ourselves.
996
10.1k
    pub fn process_read_record<'b>(&mut self, r: &RpcReplyPacket<'b>,
997
10.1k
            reply: &NfsReplyRead<'b>, xidmapr: Option<&NFSRequestXidMap>) -> u32
998
    {
999
        let file_name;
1000
        let file_handle;
1001
        let chunk_offset;
1002
        let nfs_version;
1003
1004
10.1k
        let mut fill_bytes = 0;
1005
10.1k
        let pad = reply.count % 4;
1006
10.1k
        if pad != 0 {
1007
7.26k
            fill_bytes = 4 - pad;
1008
7.26k
        }
1009
1010
        // linux defines a max of 1mb. Allow several multiples.
1011
10.1k
        if reply.count == 0 || reply.count > 16777216 {
1012
2.19k
            return 0;
1013
7.96k
        }
1014
1015
7.96k
        match xidmapr {
1016
5.87k
            Some(xidmap) => {
1017
5.87k
                file_name = xidmap.file_name.to_vec();
1018
5.87k
                file_handle = xidmap.file_handle.to_vec();
1019
5.87k
                chunk_offset = xidmap.chunk_offset;
1020
5.87k
                nfs_version = xidmap.progver;
1021
5.87k
            },
1022
            None => {
1023
2.08k
                if let Some(xidmap) = self.requestmap.get(&r.hdr.xid) {
1024
2.08k
                    file_name = xidmap.file_name.to_vec();
1025
2.08k
                    file_handle = xidmap.file_handle.to_vec();
1026
2.08k
                    chunk_offset = xidmap.chunk_offset;
1027
2.08k
                    nfs_version = xidmap.progver;
1028
2.08k
                } else {
1029
0
                    return 0;
1030
                }
1031
            },
1032
        }
1033
        SCLogDebug!("chunk_offset {}", chunk_offset);
1034
1035
7.96k
        let mut is_last = reply.eof;
1036
        SCLogDebug!("XID {} is_last {} fill_bytes {} reply.count {} reply.data_len {} reply.data.len() {}",
1037
                r.hdr.xid, is_last, fill_bytes, reply.count, reply.data_len, reply.data.len());
1038
1039
7.96k
        if nfs_version == 2 {
1040
4.20k
            let size = match parse_nfs2_attribs(reply.attr_blob) {
1041
4.20k
                Ok((_, ref attr)) => attr.asize,
1042
0
                _ => 0,
1043
            };
1044
            SCLogDebug!("NFSv2 READ reply record: File size {}. Offset {} data len {}: total {}",
1045
                    size, chunk_offset, reply.data_len, chunk_offset + reply.data_len as u64);
1046
1047
4.20k
            if size as u64 == chunk_offset + reply.data_len as u64 {
1048
1.54k
                is_last = true;
1049
2.66k
            }
1050
1051
3.75k
        }
1052
1053
7.96k
        let is_partial = reply.data.len() < reply.count as usize;
1054
        SCLogDebug!("partial data? {}", is_partial);
1055
1056
7.96k
        let found = match self.get_file_tx_by_handle(&file_handle, Direction::ToClient) {
1057
5.55k
            Some(tx) => {
1058
                SCLogDebug!("updated TX {:?}", tx);
1059
5.55k
                if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
1060
5.55k
                    filetracker_newchunk(&mut tdf.file_tracker,
1061
5.55k
                            &file_name, reply.data, chunk_offset,
1062
5.55k
                            reply.count, fill_bytes as u8, is_last, &r.hdr.xid);
1063
5.55k
                    tdf.chunk_count += 1;
1064
5.55k
                    if is_last {
1065
2.45k
                        tdf.file_last_xid = r.hdr.xid;
1066
2.45k
                        tx.rpc_response_status = r.reply_state;
1067
2.45k
                        tx.nfs_response_status = reply.status;
1068
2.45k
                        tx.is_last = true;
1069
2.45k
                        tx.request_done = true;
1070
1071
                        /* if this is a partial record we will close the tx
1072
                         * when we've received the final data */
1073
2.45k
                        if !is_partial {
1074
1.80k
                            tx.response_done = true;
1075
1.80k
                            SCLogDebug!("TX {} is DONE", tx.id);
1076
1.80k
                        }
1077
3.10k
                    }
1078
5.55k
                    true
1079
                } else {
1080
0
                    false
1081
                }
1082
            },
1083
2.40k
            None => { false },
1084
        };
1085
7.96k
        if !found {
1086
2.40k
            let tx = self.new_file_tx(&file_handle, &file_name, Direction::ToClient);
1087
2.40k
            if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
1088
2.40k
                filetracker_newchunk(&mut tdf.file_tracker,
1089
2.40k
                        &file_name, reply.data, chunk_offset,
1090
2.40k
                        reply.count, fill_bytes as u8, is_last, &r.hdr.xid);
1091
2.40k
                tx.procedure = if nfs_version < 4 { NFSPROC3_READ } else { NFSPROC4_READ };
1092
2.40k
                tx.xid = r.hdr.xid;
1093
2.40k
                tx.is_first = true;
1094
2.40k
                if is_last {
1095
1.97k
                    tdf.file_last_xid = r.hdr.xid;
1096
1.97k
                    tx.rpc_response_status = r.reply_state;
1097
1.97k
                    tx.nfs_response_status = reply.status;
1098
1.97k
                    tx.is_last = true;
1099
1.97k
                    tx.request_done = true;
1100
1101
                    /* if this is a partial record we will close the tx
1102
                     * when we've received the final data */
1103
1.97k
                    if !is_partial {
1104
1.70k
                        tx.response_done = true;
1105
1.70k
                        SCLogDebug!("TX {} is DONE", tx.id);
1106
1.70k
                    }
1107
432
                }
1108
0
            }
1109
5.55k
        }
1110
1111
7.96k
        if !self.is_udp {
1112
2.11k
            self.tc_chunk_xid = r.hdr.xid;
1113
2.11k
            debug_validate_bug_on!(reply.data.len() as u32 > reply.count);
1114
2.11k
            self.tc_chunk_left = reply.count - reply.data.len() as u32;
1115
5.85k
        }
1116
1117
        SCLogDebug!("REPLY {} to procedure {} blob size {} / {}: chunk_left {} chunk_xid {:04X}",
1118
                r.hdr.xid, NFSPROC3_READ, r.prog_data.len(), reply.count, self.tc_chunk_left,
1119
                self.tc_chunk_xid);
1120
7.96k
        0
1121
10.1k
    }
1122
1123
2.84k
    fn process_partial_read_reply_record<'b>(&mut self, r: &RpcReplyPacket<'b>, reply: &NfsReplyRead<'b>) -> u32 {
1124
        SCLogDebug!("REPLY {} to procedure READ blob size {} / {}",
1125
                r.hdr.xid, r.prog_data.len(), reply.count);
1126
1127
2.84k
        return self.process_read_record(r, reply, None);
1128
2.84k
    }
1129
1130
22.2k
    fn peek_reply_record(&mut self, r: &RpcPacketHeader) -> u32 {
1131
22.2k
        if let Some(xidmap) = self.requestmap.get(&r.xid) {
1132
7.39k
            return xidmap.procedure;
1133
        } else {
1134
            SCLogDebug!("REPLY: xid {} NOT FOUND", r.xid);
1135
14.8k
            return 0;
1136
        }
1137
22.2k
    }
1138
1139
134
    pub fn parse_tcp_data_ts_gap(&mut self, gap_size: u32) -> AppLayerResult {
1140
        SCLogDebug!("parse_tcp_data_ts_gap ({})", gap_size);
1141
134
        let gap = vec![0; gap_size as usize];
1142
134
        let consumed = self.filetracker_update(Direction::ToServer, &gap, gap_size);
1143
134
        if consumed > gap_size {
1144
            SCLogDebug!("consumed more than GAP size: {} > {}", consumed, gap_size);
1145
0
            return AppLayerResult::ok();
1146
134
        }
1147
134
        self.ts_ssn_gap = true;
1148
134
        self.ts_gap = true;
1149
        SCLogDebug!("parse_tcp_data_ts_gap ({}) done", gap_size);
1150
134
        return AppLayerResult::ok();
1151
134
    }
1152
1153
150
    pub fn parse_tcp_data_tc_gap(&mut self, gap_size: u32) -> AppLayerResult {
1154
        SCLogDebug!("parse_tcp_data_tc_gap ({})", gap_size);
1155
150
        let gap = vec![0; gap_size as usize];
1156
150
        let consumed = self.filetracker_update(Direction::ToClient, &gap, gap_size);
1157
150
        if consumed > gap_size {
1158
            SCLogDebug!("consumed more than GAP size: {} > {}", consumed, gap_size);
1159
0
            return AppLayerResult::ok();
1160
150
        }
1161
150
        self.tc_ssn_gap = true;
1162
150
        self.tc_gap = true;
1163
        SCLogDebug!("parse_tcp_data_tc_gap ({}) done", gap_size);
1164
150
        return AppLayerResult::ok();
1165
150
    }
1166
1167
    /// Handle partial records
1168
314k
    fn parse_tcp_partial_data_ts<'b>(&mut self, base_input: &'b[u8], cur_i: &'b[u8],
1169
314k
            phdr: &RpcRequestPacketPartial, rec_size: usize) -> AppLayerResult {
1170
        // special case: avoid buffering file write blobs
1171
        // as these can be large.
1172
314k
        if rec_size >= 512 && cur_i.len() >= 44 {
1173
            // large record, likely file xfer
1174
            SCLogDebug!("large record {}, likely file xfer", rec_size);
1175
1176
            // quick peek, are we in WRITE mode?
1177
183k
            if phdr.procedure == NFSPROC3_WRITE {
1178
                SCLogDebug!("CONFIRMED WRITE: large record {}, file chunk xfer", rec_size);
1179
1180
                // lets try to parse the RPC record. Might fail with Incomplete.
1181
81.8k
                match parse_rpc(cur_i, false) {
1182
76.4k
                    Ok((_rem, ref hdr)) => {
1183
                        // we got here because rec_size > input, so we should never have
1184
                        // remaining data
1185
76.4k
                        debug_validate_bug_on!(!_rem.is_empty());
1186
1187
76.4k
                        match parse_nfs3_request_write(hdr.prog_data, false) {
1188
71.4k
                            Ok((_, ref w)) => {
1189
                                // deal with the partial nfs write data
1190
71.4k
                                self.process_partial_write_request_record(hdr, w);
1191
71.4k
                                return AppLayerResult::ok();
1192
                            }
1193
73
                            Err(Err::Error(_e)) | Err(Err::Failure(_e)) => {
1194
73
                                self.set_event(NFSEvent::MalformedData);
1195
                                SCLogDebug!("Parsing failed: {:?}", _e);
1196
73
                                return AppLayerResult::err();
1197
                            }
1198
4.90k
                            Err(Err::Incomplete(_)) => {
1199
4.90k
                                // this is normal, fall through to incomplete handling
1200
4.90k
                            }
1201
                        }
1202
                    }
1203
5.29k
                    Err(Err::Incomplete(_)) => {
1204
5.29k
                        // size check was done for a minimal RPC record size,
1205
5.29k
                        // so Incomplete is normal.
1206
5.29k
                        SCLogDebug!("TS data incomplete");
1207
5.29k
                    }
1208
58
                    Err(Err::Error(_e)) | Err(Err::Failure(_e)) => {
1209
58
                        self.set_event(NFSEvent::MalformedData);
1210
                        SCLogDebug!("Parsing failed: {:?}", _e);
1211
58
                        return AppLayerResult::err();
1212
                    }
1213
                }
1214
101k
            }
1215
130k
        }
1216
        // make sure we pass a value higher than current input
1217
        // but lower than the record size
1218
242k
        let n1 = cmp::max(cur_i.len(), 1024);
1219
242k
        let n2 = cmp::min(n1, rec_size);
1220
242k
        return AppLayerResult::incomplete((base_input.len() - cur_i.len()) as u32, n2 as u32);
1221
314k
    }
1222
1223
    /// Parsing function, handling TCP chunks fragmentation
1224
651k
    pub fn parse_tcp_data_ts(&mut self, flow: *const Flow, stream_slice: &StreamSlice) -> AppLayerResult {
1225
651k
        let mut cur_i = stream_slice.as_slice();
1226
        // take care of in progress file chunk transfers
1227
        // and skip buffer beyond it
1228
651k
        let consumed = self.filetracker_update(Direction::ToServer, cur_i, 0);
1229
651k
        if consumed > 0 {
1230
13.8k
            if consumed > cur_i.len() as u32 {
1231
0
                return AppLayerResult::err();
1232
13.8k
            }
1233
13.8k
            cur_i = &cur_i[consumed as usize..];
1234
637k
        }
1235
651k
        if cur_i.is_empty() {
1236
10.5k
            return AppLayerResult::ok();
1237
640k
        }
1238
640k
        if self.ts_gap {
1239
            SCLogDebug!("TS trying to catch up after GAP (input {})", cur_i.len());
1240
1241
178
            let mut _cnt = 0;
1242
3.80k
            while !cur_i.is_empty() {
1243
3.80k
                _cnt += 1;
1244
3.80k
                match nfs_probe(cur_i, Direction::ToServer) {
1245
                    1 => {
1246
                        SCLogDebug!("expected data found");
1247
78
                        self.ts_gap = false;
1248
78
                        break;
1249
                    },
1250
                    0 => {
1251
                        SCLogDebug!("incomplete, queue and retry with the next block (input {}). Looped {} times.",
1252
                                cur_i.len(), _cnt);
1253
100
                        return AppLayerResult::incomplete(stream_slice.len() - cur_i.len() as u32, (cur_i.len() + 1) as u32);
1254
                    },
1255
                    -1 => {
1256
3.62k
                        cur_i = &cur_i[1..];
1257
3.62k
                        if cur_i.is_empty() {
1258
0
                            SCLogDebug!("all post-GAP data in this chunk was bad. Looped {} times.", _cnt);
1259
3.62k
                        }
1260
                    },
1261
                    _ => {
1262
0
                        return AppLayerResult::err();
1263
                    },
1264
                }
1265
            }
1266
            SCLogDebug!("TS GAP handling done (input {})", cur_i.len());
1267
640k
        }
1268
1269
946k
        while !cur_i.is_empty() { // min record size
1270
900k
            self.add_rpc_tcp_ts_pdu(flow, stream_slice, cur_i, cur_i.len() as i64);
1271
900k
            match parse_rpc_request_partial(cur_i) {
1272
620k
                Ok((_, ref rpc_phdr)) => {
1273
620k
                    let rec_size = (rpc_phdr.hdr.frag_len + 4) as usize;
1274
1275
                    // Handle partial records
1276
620k
                    if rec_size > cur_i.len() {
1277
314k
                        return self.parse_tcp_partial_data_ts(stream_slice.as_slice(), cur_i, rpc_phdr, rec_size);
1278
305k
                    }
1279
1280
                    // we have the full records size worth of data,
1281
                    // let's parse it. Errors lead to event, but are
1282
                    // not fatal as we already have enough info to
1283
                    // go to the next record.
1284
305k
                    match parse_rpc(cur_i, true) {
1285
241k
                        Ok((_, ref rpc_record)) => {
1286
241k
                            self.add_rpc_tcp_ts_creds(flow, stream_slice, &cur_i[RPC_TCP_PRE_CREDS..], (rpc_record.creds_len + 8) as i64);
1287
241k
                            self.process_request_record(flow, stream_slice, rpc_record);
1288
241k
                        }
1289
10.4k
                        Err(Err::Incomplete(_)) => {
1290
10.4k
                            self.set_event(NFSEvent::MalformedData);
1291
10.4k
                        }
1292
53.7k
                        Err(Err::Error(_e)) |
1293
53.7k
                        Err(Err::Failure(_e)) => {
1294
53.7k
                            self.set_event(NFSEvent::MalformedData);
1295
53.7k
                            SCLogDebug!("Parsing failed: {:?}", _e);
1296
53.7k
                        }
1297
                    }
1298
305k
                    cur_i = &cur_i[rec_size..];
1299
                }
1300
278k
                Err(Err::Incomplete(needed)) => {
1301
278k
                    if let Needed::Size(n) = needed {
1302
                        SCLogDebug!("Not enough data for partial RPC header {:?}", needed);
1303
                        // 28 is the partial RPC header size parse_rpc_request_partial
1304
                        // looks for.
1305
278k
                        let n = usize::from(n);
1306
278k
                        let need = if n > 28 { n } else { 28 };
1307
278k
                        return AppLayerResult::incomplete(stream_slice.len() - cur_i.len() as u32, need as u32);
1308
0
                    }
1309
0
                    return AppLayerResult::err();
1310
                }
1311
                /* This error is fatal. If we failed to parse the RPC hdr we don't
1312
                 * have a length and we don't know where the next record starts. */
1313
2.04k
                Err(Err::Error(_e)) |
1314
0
                Err(Err::Failure(_e)) => {
1315
2.04k
                    self.set_event(NFSEvent::MalformedData);
1316
                    SCLogDebug!("Parsing failed: {:?}", _e);
1317
2.04k
                    return AppLayerResult::err();
1318
                }
1319
            }
1320
        };
1321
1322
45.6k
        self.post_gap_housekeeping(Direction::ToServer);
1323
45.6k
        if self.check_post_gap_file_txs && !self.post_gap_files_checked {
1324
0
            self.post_gap_housekeeping_for_files();
1325
0
            self.post_gap_files_checked = true;
1326
45.6k
        }
1327
1328
45.6k
        AppLayerResult::ok()
1329
651k
    }
1330
1331
    /// Handle partial records
1332
150k
    fn parse_tcp_partial_data_tc<'b>(&mut self, base_input: &'b[u8], cur_i: &'b[u8],
1333
150k
            phdr: &RpcPacketHeader, rec_size: usize) -> AppLayerResult {
1334
        // special case: avoid buffering file read blobs
1335
        // as these can be large.
1336
150k
        if rec_size >= 512 && cur_i.len() >= 128 {//36 {
1337
            // large record, likely file xfer
1338
            SCLogDebug!("large record {}, likely file xfer", rec_size);
1339
1340
            // quick peek, are in READ mode?
1341
22.2k
            if self.peek_reply_record(phdr) == NFSPROC3_READ {
1342
                SCLogDebug!("CONFIRMED large READ record {}, likely file chunk xfer", rec_size);
1343
1344
                // we should have enough data to parse the RPC record
1345
3.60k
                match parse_rpc_reply(cur_i, false) {
1346
3.38k
                    Ok((_rem, ref hdr)) => {
1347
                        // we got here because rec_size > input, so we should never have
1348
                        // remaining data
1349
3.38k
                        debug_validate_bug_on!(!_rem.is_empty());
1350
1351
3.38k
                        match parse_nfs3_reply_read(hdr.prog_data, false) {
1352
2.84k
                            Ok((_, ref r)) => {
1353
                                // deal with the partial nfs read data
1354
2.84k
                                self.process_partial_read_reply_record(hdr, r);
1355
2.84k
                                return AppLayerResult::ok();
1356
                            }
1357
45
                            Err(Err::Error(_e)) | Err(Err::Failure(_e)) => {
1358
45
                                self.set_event(NFSEvent::MalformedData);
1359
                                SCLogDebug!("Parsing failed: {:?}", _e);
1360
45
                                return AppLayerResult::err();
1361
                            }
1362
497
                            Err(Err::Incomplete(_)) => {
1363
497
                                // this is normal, fall through to incomplete handling
1364
497
                            }
1365
                        }
1366
                    }
1367
204
                    Err(Err::Incomplete(_)) => {
1368
204
                        // size check was done for a minimal RPC record size,
1369
204
                        // so Incomplete is normal.
1370
204
                        SCLogDebug!("TC data incomplete");
1371
204
                    }
1372
7
                    Err(Err::Error(_e)) | Err(Err::Failure(_e)) => {
1373
7
                        self.set_event(NFSEvent::MalformedData);
1374
                        SCLogDebug!("Parsing failed: {:?}", _e);
1375
7
                        return AppLayerResult::err();
1376
                    }
1377
                }
1378
18.6k
            }
1379
128k
        }
1380
        // make sure we pass a value higher than current input
1381
        // but lower than the record size
1382
148k
        let n1 = cmp::max(cur_i.len(), 1024);
1383
148k
        let n2 = cmp::min(n1, rec_size);
1384
148k
        return AppLayerResult::incomplete((base_input.len() - cur_i.len()) as u32, n2 as u32);
1385
150k
    }
1386
1387
    /// Parsing function, handling TCP chunks fragmentation
1388
598k
    pub fn parse_tcp_data_tc(&mut self, flow: *const Flow, stream_slice: &StreamSlice) -> AppLayerResult {
1389
598k
        let mut cur_i = stream_slice.as_slice();
1390
        // take care of in progress file chunk transfers
1391
        // and skip buffer beyond it
1392
598k
        let consumed = self.filetracker_update(Direction::ToClient, cur_i, 0);
1393
598k
        if consumed > 0 {
1394
38.4k
            if consumed > cur_i.len() as u32 {
1395
0
                return AppLayerResult::err();
1396
38.4k
            }
1397
38.4k
            cur_i = &cur_i[consumed as usize..];
1398
559k
        }
1399
598k
        if cur_i.is_empty() {
1400
38.0k
            return AppLayerResult::ok();
1401
560k
        }
1402
560k
        if self.tc_gap {
1403
            SCLogDebug!("TC trying to catch up after GAP (input {})", cur_i.len());
1404
1405
6.84k
            let mut _cnt = 0;
1406
4.07M
            while !cur_i.is_empty() {
1407
4.07M
                _cnt += 1;
1408
4.07M
                match nfs_probe(cur_i, Direction::ToClient) {
1409
                    1 => {
1410
                        SCLogDebug!("expected data found");
1411
86
                        self.tc_gap = false;
1412
86
                        break;
1413
                    },
1414
                    0 => {
1415
                        SCLogDebug!("incomplete, queue and retry with the next block (input {}). Looped {} times.",
1416
                                cur_i.len(), _cnt);
1417
6.76k
                        return AppLayerResult::incomplete(stream_slice.len() - cur_i.len() as u32, (cur_i.len() + 1) as u32);
1418
                    },
1419
                    -1 => {
1420
4.06M
                        cur_i = &cur_i[1..];
1421
4.06M
                        if cur_i.is_empty() {
1422
0
                            SCLogDebug!("all post-GAP data in this chunk was bad. Looped {} times.", _cnt);
1423
4.06M
                        }
1424
                    },
1425
                    _ => {
1426
0
                        return AppLayerResult::err();
1427
                    }
1428
                }
1429
            }
1430
            SCLogDebug!("TC GAP handling done (input {})", cur_i.len());
1431
553k
        }
1432
1433
686k
        while !cur_i.is_empty() {
1434
666k
            self.add_rpc_tcp_tc_pdu(flow, stream_slice, cur_i, cur_i.len() as i64);
1435
666k
            match parse_rpc_packet_header(cur_i) {
1436
283k
                Ok((_, ref rpc_phdr)) => {
1437
283k
                    let rec_size = (rpc_phdr.frag_len + 4) as usize;
1438
                    // see if we have all data available
1439
283k
                    if rec_size > cur_i.len() {
1440
150k
                        return self.parse_tcp_partial_data_tc(stream_slice.as_slice(), cur_i, rpc_phdr, rec_size);
1441
132k
                    }
1442
1443
                    // we have the full data of the record, lets parse
1444
132k
                    match parse_rpc_reply(cur_i, true) {
1445
85.8k
                        Ok((_, ref rpc_record)) => {
1446
85.8k
                            self.add_rpc_tcp_tc_frames(flow, stream_slice, cur_i, cur_i.len() as i64);
1447
85.8k
                            self.process_reply_record(flow, stream_slice, rpc_record);
1448
85.8k
                        }
1449
4.07k
                        Err(Err::Incomplete(_)) => {
1450
4.07k
                            // we shouldn't get incomplete as we have the full data
1451
4.07k
                            // so if we got incomplete anyway it's the data that is
1452
4.07k
                            // bad.
1453
4.07k
                            self.set_event(NFSEvent::MalformedData);
1454
4.07k
                        }
1455
42.9k
                        Err(Err::Error(_e)) |
1456
42.9k
                        Err(Err::Failure(_e)) => {
1457
42.9k
                            self.set_event(NFSEvent::MalformedData);
1458
42.9k
                            SCLogDebug!("Parsing failed: {:?}", _e);
1459
42.9k
                        }
1460
                    }
1461
132k
                    cur_i = &cur_i[rec_size..]; // progress input past parsed record
1462
                }
1463
380k
                Err(Err::Incomplete(needed)) => {
1464
380k
                    if let Needed::Size(n) = needed {
1465
                        SCLogDebug!("Not enough data for partial RPC header {:?}", needed);
1466
                        // 12 is the partial RPC header size parse_rpc_packet_header
1467
                        // looks for.
1468
380k
                        let n = usize::from(n);
1469
380k
                        let need = if n > 12 { n } else { 12 };
1470
380k
                        return AppLayerResult::incomplete(stream_slice.len() - cur_i.len() as u32, need as u32);
1471
0
                    }
1472
0
                    return AppLayerResult::err();
1473
                }
1474
                /* This error is fatal. If we failed to parse the RPC hdr we don't
1475
                 * have a length and we don't know where the next record starts. */
1476
1.32k
                Err(Err::Error(_e)) |
1477
0
                Err(Err::Failure(_e)) => {
1478
1.32k
                    self.set_event(NFSEvent::MalformedData);
1479
                    SCLogDebug!("Parsing failed: {:?}", _e);
1480
1.32k
                    return AppLayerResult::err();
1481
                }
1482
            }
1483
        };
1484
20.1k
        self.post_gap_housekeeping(Direction::ToClient);
1485
20.1k
        if self.check_post_gap_file_txs && !self.post_gap_files_checked {
1486
0
            self.post_gap_housekeeping_for_files();
1487
0
            self.post_gap_files_checked = true;
1488
20.1k
        }
1489
20.1k
        AppLayerResult::ok()
1490
598k
    }
1491
    /// Parsing function
1492
988k
    pub fn parse_udp_ts(&mut self, flow: *const Flow, stream_slice: &StreamSlice) -> AppLayerResult {
1493
988k
        let input = stream_slice.as_slice();
1494
        SCLogDebug!("parse_udp_ts ({})", input.len());
1495
988k
        self.add_rpc_udp_ts_pdu(flow, stream_slice, input, input.len() as i64);
1496
988k
        if !input.is_empty() {
1497
988k
            match parse_rpc_udp_request(input) {
1498
470k
                Ok((_, ref rpc_record)) => {
1499
470k
                    self.is_udp = true;
1500
470k
                    self.add_rpc_udp_ts_creds(flow, stream_slice, &input[RPC_UDP_PRE_CREDS..], (rpc_record.creds_len + 8) as i64);
1501
470k
                    match rpc_record.progver {
1502
400k
                        3 => {
1503
400k
                            self.process_request_record(flow, stream_slice, rpc_record);
1504
400k
                        },
1505
37.3k
                        2 => {
1506
37.3k
                            self.add_nfs_ts_frame(flow, stream_slice, rpc_record.prog_data, rpc_record.prog_data_size as i64);
1507
37.3k
                            self.process_request_record_v2(rpc_record);
1508
37.3k
                        },
1509
33.0k
                        _ => { },
1510
                    }
1511
                },
1512
223k
                Err(Err::Incomplete(_)) => {
1513
223k
                },
1514
293k
                Err(Err::Error(_e)) |
1515
293k
                Err(Err::Failure(_e)) => {
1516
293k
                    SCLogDebug!("Parsing failed: {:?}", _e);
1517
293k
                }
1518
            }
1519
0
        }
1520
988k
        AppLayerResult::ok()
1521
988k
    }
1522
1523
    /// Parsing function
1524
813k
    pub fn parse_udp_tc(&mut self, flow: *const Flow, stream_slice: &StreamSlice) -> AppLayerResult {
1525
813k
        let input = stream_slice.as_slice();
1526
        SCLogDebug!("parse_udp_tc ({})", input.len());
1527
813k
        self.add_rpc_udp_tc_pdu(flow, stream_slice, input, input.len() as i64);
1528
813k
        if !input.is_empty() {
1529
813k
            match parse_rpc_udp_reply(input) {
1530
129k
                Ok((_, ref rpc_record)) => {
1531
129k
                    self.is_udp = true;
1532
129k
                    self.add_rpc_udp_tc_frames(flow, stream_slice, input, input.len() as i64);
1533
129k
                    self.process_reply_record(flow, stream_slice, rpc_record);
1534
129k
                },
1535
244k
                Err(Err::Incomplete(_)) => {
1536
244k
                },
1537
439k
                Err(Err::Error(_e)) |
1538
439k
                Err(Err::Failure(_e)) => {
1539
439k
                    SCLogDebug!("Parsing failed: {:?}", _e);
1540
439k
                }
1541
            }
1542
0
        }
1543
813k
        AppLayerResult::ok()
1544
813k
    }
1545
}
1546
1547
/// Returns *mut NFSState
1548
#[no_mangle]
1549
22.3k
pub extern "C" fn rs_nfs_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
1550
22.3k
    let state = NFSState::new();
1551
22.3k
    let boxed = Box::new(state);
1552
    SCLogDebug!("allocating state");
1553
22.3k
    return Box::into_raw(boxed) as *mut _;
1554
22.3k
}
1555
1556
/// Params:
1557
/// - state: *mut NFSState as void pointer
1558
#[no_mangle]
1559
22.3k
pub extern "C" fn rs_nfs_state_free(state: *mut std::os::raw::c_void) {
1560
    // Just unbox...
1561
    SCLogDebug!("freeing state");
1562
22.3k
    std::mem::drop(unsafe { Box::from_raw(state as *mut NFSState) });
1563
22.3k
}
1564
1565
/// C binding parse a NFS TCP request. Returns 1 on success, -1 on failure.
1566
#[no_mangle]
1567
651k
pub unsafe extern "C" fn rs_nfs_parse_request(flow: *const Flow,
1568
651k
                                       state: *mut std::os::raw::c_void,
1569
651k
                                       _pstate: *mut std::os::raw::c_void,
1570
651k
                                       stream_slice: StreamSlice,
1571
651k
                                       _data: *const std::os::raw::c_void,
1572
651k
                                       ) -> AppLayerResult
1573
{
1574
651k
    let state = cast_pointer!(state, NFSState);
1575
651k
    let flow = cast_pointer!(flow, Flow);
1576
1577
651k
    if stream_slice.is_gap() {
1578
134
        return rs_nfs_parse_request_tcp_gap(state, stream_slice.gap_size());
1579
651k
    }
1580
    SCLogDebug!("parsing {} bytes of request data", stream_slice.len());
1581
1582
651k
    state.update_ts(flow.get_last_time().as_secs());
1583
651k
    state.parse_tcp_data_ts(flow, &stream_slice)
1584
651k
}
1585
1586
#[no_mangle]
1587
134
pub extern "C" fn rs_nfs_parse_request_tcp_gap(
1588
134
                                        state: &mut NFSState,
1589
134
                                        input_len: u32)
1590
134
                                        -> AppLayerResult
1591
{
1592
134
    state.parse_tcp_data_ts_gap(input_len)
1593
134
}
1594
1595
#[no_mangle]
1596
598k
pub unsafe extern "C" fn rs_nfs_parse_response(flow: *const Flow,
1597
598k
                                        state: *mut std::os::raw::c_void,
1598
598k
                                        _pstate: *mut std::os::raw::c_void,
1599
598k
                                        stream_slice: StreamSlice,
1600
598k
                                        _data: *const std::os::raw::c_void,
1601
598k
                                        ) -> AppLayerResult
1602
{
1603
598k
    let state = cast_pointer!(state, NFSState);
1604
598k
    let flow = cast_pointer!(flow, Flow);
1605
1606
598k
    if stream_slice.is_gap() {
1607
150
        return rs_nfs_parse_response_tcp_gap(state, stream_slice.gap_size());
1608
598k
    }
1609
    SCLogDebug!("parsing {} bytes of response data", stream_slice.len());
1610
1611
598k
    state.update_ts(flow.get_last_time().as_secs());
1612
598k
    state.parse_tcp_data_tc(flow, &stream_slice)
1613
598k
}
1614
1615
#[no_mangle]
1616
150
pub extern "C" fn rs_nfs_parse_response_tcp_gap(
1617
150
                                        state: &mut NFSState,
1618
150
                                        input_len: u32)
1619
150
                                        -> AppLayerResult
1620
{
1621
150
    state.parse_tcp_data_tc_gap(input_len)
1622
150
}
1623
1624
/// C binding to parse an NFS/UDP request. Returns 1 on success, -1 on failure.
1625
#[no_mangle]
1626
988k
pub unsafe extern "C" fn rs_nfs_parse_request_udp(f: *const Flow,
1627
988k
                                       state: *mut std::os::raw::c_void,
1628
988k
                                       _pstate: *mut std::os::raw::c_void,
1629
988k
                                       stream_slice: StreamSlice,
1630
988k
                                       _data: *const std::os::raw::c_void,
1631
988k
                                       ) -> AppLayerResult
1632
{
1633
988k
    let state = cast_pointer!(state, NFSState);
1634
1635
    SCLogDebug!("parsing {} bytes of request data", stream_slice.len());
1636
988k
    state.parse_udp_ts(f, &stream_slice)
1637
988k
}
1638
1639
#[no_mangle]
1640
813k
pub unsafe extern "C" fn rs_nfs_parse_response_udp(f: *const Flow,
1641
813k
                                        state: *mut std::os::raw::c_void,
1642
813k
                                        _pstate: *mut std::os::raw::c_void,
1643
813k
                                        stream_slice: StreamSlice,
1644
813k
                                        _data: *const std::os::raw::c_void,
1645
813k
                                        ) -> AppLayerResult
1646
{
1647
813k
    let state = cast_pointer!(state, NFSState);
1648
    SCLogDebug!("parsing {} bytes of response data", stream_slice.len());
1649
813k
    state.parse_udp_tc(f, &stream_slice)
1650
813k
}
1651
1652
#[no_mangle]
1653
9.19M
pub unsafe extern "C" fn rs_nfs_state_get_tx_count(state: *mut std::os::raw::c_void)
1654
9.19M
                                            -> u64
1655
{
1656
9.19M
    let state = cast_pointer!(state, NFSState);
1657
    SCLogDebug!("rs_nfs_state_get_tx_count: returning {}", state.tx_id);
1658
9.19M
    return state.tx_id;
1659
9.19M
}
1660
1661
#[no_mangle]
1662
16.2k
pub unsafe extern "C" fn rs_nfs_state_get_tx(state: *mut std::os::raw::c_void,
1663
16.2k
                                      tx_id: u64)
1664
16.2k
                                      -> *mut std::os::raw::c_void
1665
{
1666
16.2k
    let state = cast_pointer!(state, NFSState);
1667
16.2k
    match state.get_tx_by_id(tx_id) {
1668
13.2k
        Some(tx) => {
1669
13.2k
            return tx as *const _ as *mut _;
1670
        }
1671
        None => {
1672
2.98k
            return std::ptr::null_mut();
1673
        }
1674
    }
1675
16.2k
}
1676
1677
#[no_mangle]
1678
141k
pub unsafe extern "C" fn rs_nfs_state_tx_free(state: *mut std::os::raw::c_void,
1679
141k
                                       tx_id: u64)
1680
{
1681
141k
    let state = cast_pointer!(state, NFSState);
1682
141k
    state.free_tx(tx_id);
1683
141k
}
1684
1685
#[no_mangle]
1686
396M
pub unsafe extern "C" fn rs_nfs_tx_get_alstate_progress(tx: *mut std::os::raw::c_void,
1687
396M
                                                  direction: u8)
1688
396M
                                                  -> std::os::raw::c_int
1689
{
1690
396M
    let tx = cast_pointer!(tx, NFSTransaction);
1691
396M
    if direction == Direction::ToServer.into() && tx.request_done {
1692
        SCLogDebug!("TOSERVER progress 1");
1693
1.04M
        return 1;
1694
395M
    } else if direction == Direction::ToClient.into() && tx.response_done {
1695
        SCLogDebug!("TOCLIENT progress 1");
1696
28.4M
        return 1;
1697
    } else {
1698
        SCLogDebug!("{} progress 0", direction);
1699
366M
        return 0;
1700
    }
1701
396M
}
1702
1703
#[no_mangle]
1704
367M
pub unsafe extern "C" fn rs_nfs_get_tx_data(
1705
367M
    tx: *mut std::os::raw::c_void)
1706
367M
    -> *mut AppLayerTxData
1707
{
1708
367M
    let tx = cast_pointer!(tx, NFSTransaction);
1709
367M
    return &mut tx.tx_data;
1710
367M
}
1711
1712
export_state_data_get!(rs_nfs_get_state_data, NFSState);
1713
1714
/// return procedure(s) in the tx. At 0 return the main proc,
1715
/// otherwise get procs from the 'file_additional_procs'.
1716
/// Keep calling until 0 is returned.
1717
#[no_mangle]
1718
8.66k
pub unsafe extern "C" fn rs_nfs_tx_get_procedures(tx: &mut NFSTransaction,
1719
8.66k
                                           i: u16,
1720
8.66k
                                           procedure: *mut u32)
1721
8.66k
                                           -> u8
1722
{
1723
8.66k
    if i == 0 {
1724
5.95k
        *procedure = tx.procedure;
1725
5.95k
        return 1;
1726
2.71k
    }
1727
1728
2.71k
    if !tx.is_file_tx {
1729
2.64k
        return 0;
1730
70
    }
1731
1732
    /* file tx handling follows */
1733
1734
70
    if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
1735
70
        let idx = i as usize - 1;
1736
70
        if idx < tdf.file_additional_procs.len() {
1737
0
            let p = tdf.file_additional_procs[idx];
1738
0
            *procedure = p;
1739
0
            return 1;
1740
70
        }
1741
0
    }
1742
70
    return 0;
1743
8.66k
}
1744
1745
#[no_mangle]
1746
5.92k
pub unsafe extern "C" fn rs_nfs_tx_get_version(tx: &mut NFSTransaction,
1747
5.92k
                                        version: *mut u32)
1748
{
1749
5.92k
    *version = tx.nfs_version as u32;
1750
5.92k
}
1751
1752
#[no_mangle]
1753
68
pub unsafe extern "C" fn rs_nfs_init(context: &'static mut SuricataFileContext)
1754
{
1755
68
    SURICATA_NFS_FILE_CONFIG = Some(context);
1756
68
}
1757
1758
64.7k
fn nfs_probe_dir(i: &[u8], rdir: *mut u8) -> i8 {
1759
64.7k
    match parse_rpc_packet_header(i) {
1760
60.7k
        Ok((_, ref hdr)) => {
1761
60.7k
            let dir = if hdr.msgtype == 0 {
1762
34.4k
                Direction::ToServer
1763
            } else {
1764
26.3k
                Direction::ToClient
1765
            };
1766
60.7k
            unsafe { *rdir = dir as u8 };
1767
60.7k
            return 1;
1768
        },
1769
        Err(Err::Incomplete(_)) => {
1770
3.82k
            return 0;
1771
        },
1772
        Err(_) => {
1773
112
            return -1;
1774
        },
1775
    }
1776
64.7k
}
1777
1778
4.14M
pub fn nfs_probe(i: &[u8], direction: Direction) -> i32 {
1779
4.14M
    if direction == Direction::ToClient {
1780
4.10M
        match parse_rpc_reply(i, false) {
1781
31.7k
            Ok((_, ref rpc)) => {
1782
31.7k
                if rpc.hdr.frag_len >= 24 && rpc.hdr.frag_len <= 35000 && rpc.hdr.msgtype == 1 && rpc.reply_state == 0 && rpc.accept_state == 0 {
1783
                    SCLogDebug!("TC PROBE LEN {} XID {} TYPE {}", rpc.hdr.frag_len, rpc.hdr.xid, rpc.hdr.msgtype);
1784
12.0k
                    return 1;
1785
                } else {
1786
19.7k
                    return -1;
1787
                }
1788
            },
1789
            Err(Err::Incomplete(_)) => {
1790
25.1k
                match parse_rpc_packet_header (i) {
1791
18.3k
                    Ok((_, ref rpc_hdr)) => {
1792
18.3k
                        if rpc_hdr.frag_len >= 24 && rpc_hdr.frag_len <= 35000 && rpc_hdr.xid != 0 && rpc_hdr.msgtype == 1 {
1793
                            SCLogDebug!("TC PROBE LEN {} XID {} TYPE {}", rpc_hdr.frag_len, rpc_hdr.xid, rpc_hdr.msgtype);
1794
11.8k
                            return 1;
1795
                        } else {
1796
6.54k
                            return -1;
1797
                        }
1798
                    },
1799
                    Err(Err::Incomplete(_)) => {
1800
6.76k
                        return 0;
1801
                    },
1802
                    Err(_) => {
1803
0
                        return -1;
1804
                    },
1805
                }
1806
            },
1807
            Err(_) => {
1808
4.04M
                return -1;
1809
            },
1810
        }
1811
    } else {
1812
38.2k
        match parse_rpc(i, false) {
1813
18.8k
            Ok((_, ref rpc)) => {
1814
18.8k
                if rpc.hdr.frag_len >= 40 && rpc.hdr.msgtype == 0 &&
1815
18.8k
                   rpc.rpcver == 2 && (rpc.progver == 3 || rpc.progver == 4) &&
1816
18.5k
                   rpc.program == 100003 &&
1817
18.5k
                   rpc.procedure <= NFSPROC3_COMMIT
1818
                {
1819
18.5k
                    return rpc_auth_type_known(rpc.creds_flavor) as i32;
1820
                } else {
1821
306
                    return -1;
1822
                }
1823
            },
1824
            Err(Err::Incomplete(_)) => {
1825
15.6k
                return 0;
1826
            },
1827
            Err(_) => {
1828
3.77k
                return -1;
1829
            },
1830
        }
1831
    }
1832
4.14M
}
1833
1834
2.30k
pub fn nfs_probe_udp(i: &[u8], direction: Direction) -> i32 {
1835
2.30k
    if direction == Direction::ToClient {
1836
929
        match parse_rpc_udp_reply(i) {
1837
733
            Ok((_, ref rpc)) => {
1838
733
                if i.len() >= 32 && rpc.hdr.msgtype == 1 && rpc.reply_state == 0 && rpc.accept_state == 0 {
1839
                    SCLogDebug!("TC PROBE LEN {} XID {} TYPE {}", rpc.hdr.frag_len, rpc.hdr.xid, rpc.hdr.msgtype);
1840
477
                    return 1;
1841
                } else {
1842
256
                    return -1;
1843
                }
1844
            },
1845
            Err(_) => {
1846
196
                return -1;
1847
            },
1848
        }
1849
    } else {
1850
1.37k
        match parse_rpc_udp_request(i) {
1851
853
            Ok((_, ref rpc)) => {
1852
853
                if i.len() >= 48 && rpc.hdr.msgtype == 0 && rpc.progver == 3 && rpc.program == 100003 {
1853
486
                    return 1;
1854
367
                } else if i.len() >= 48 && rpc.hdr.msgtype == 0 && rpc.progver == 2 && rpc.program == 100003 {
1855
                    SCLogDebug!("NFSv2!");
1856
74
                    return 1;
1857
                } else {
1858
293
                    return -1;
1859
                }
1860
            },
1861
            Err(_) => {
1862
522
                return -1;
1863
            },
1864
        }
1865
    }
1866
2.30k
}
1867
1868
/// MIDSTREAM
1869
#[no_mangle]
1870
64.7k
pub unsafe extern "C" fn rs_nfs_probe_ms(
1871
64.7k
        _flow: *const Flow,
1872
64.7k
        direction: u8, input: *const u8,
1873
64.7k
        len: u32, rdir: *mut u8) -> AppProto
1874
{
1875
64.7k
    if input.is_null() {
1876
77
        return ALPROTO_UNKNOWN;
1877
64.7k
    }
1878
64.7k
    let slice: &[u8] = build_slice!(input, len as usize);
1879
    SCLogDebug!("rs_nfs_probe_ms: probing direction {:02x}", direction);
1880
64.7k
    let mut adirection : u8 = 0;
1881
64.7k
    match nfs_probe_dir(slice, &mut adirection) {
1882
        1 => {
1883
60.7k
            if adirection == Direction::ToServer.into() {
1884
34.4k
                SCLogDebug!("nfs_probe_dir said Direction::ToServer");
1885
34.4k
            } else {
1886
26.3k
                SCLogDebug!("nfs_probe_dir said Direction::ToClient");
1887
26.3k
            }
1888
60.7k
            match nfs_probe(slice, adirection.into()) {
1889
                1 => {
1890
                    SCLogDebug!("nfs_probe success: dir {:02x} adir {:02x}", direction, adirection);
1891
42.1k
                    if (direction & DIR_BOTH) != adirection {
1892
9.47k
                        *rdir = adirection;
1893
32.7k
                    }
1894
42.1k
                    ALPROTO_NFS
1895
                },
1896
15.5k
                0 => { ALPROTO_UNKNOWN },
1897
3.04k
                _ => { ALPROTO_FAILED },
1898
            }
1899
        },
1900
        0 => {
1901
3.82k
            ALPROTO_UNKNOWN
1902
        },
1903
        _ => {
1904
112
            ALPROTO_FAILED
1905
        }
1906
    }
1907
64.7k
}
1908
1909
#[no_mangle]
1910
0
pub unsafe extern "C" fn rs_nfs_probe(_f: *const Flow,
1911
0
                               direction: u8,
1912
0
                               input: *const u8,
1913
0
                               len: u32,
1914
0
                               _rdir: *mut u8)
1915
0
    -> AppProto
1916
{
1917
0
    if input.is_null() {
1918
0
        return ALPROTO_UNKNOWN;
1919
0
    }
1920
0
    let slice: &[u8] = build_slice!(input, len as usize);
1921
    SCLogDebug!("rs_nfs_probe: running probe");
1922
0
    match nfs_probe(slice, direction.into()) {
1923
0
        1 => { ALPROTO_NFS },
1924
0
        -1 => { ALPROTO_FAILED },
1925
0
        _ => { ALPROTO_UNKNOWN },
1926
    }
1927
0
}
1928
1929
/// TOSERVER probe function
1930
#[no_mangle]
1931
1.37k
pub unsafe extern "C" fn rs_nfs_probe_udp_ts(_f: *const Flow,
1932
1.37k
                               _direction: u8,
1933
1.37k
                               input: *const u8,
1934
1.37k
                               len: u32,
1935
1.37k
                               _rdir: *mut u8)
1936
1.37k
    -> AppProto
1937
{
1938
1.37k
    if input.is_null() {
1939
0
        return ALPROTO_UNKNOWN;
1940
1.37k
    }
1941
1.37k
    let slice: &[u8] = build_slice!(input, len as usize);
1942
1.37k
    match nfs_probe_udp(slice, Direction::ToServer) {
1943
560
        1 => { ALPROTO_NFS },
1944
815
        -1 => { ALPROTO_FAILED },
1945
0
        _ => { ALPROTO_UNKNOWN },
1946
    }
1947
1.37k
}
1948
1949
/// TOCLIENT probe function
1950
#[no_mangle]
1951
929
pub unsafe extern "C" fn rs_nfs_probe_udp_tc(_f: *const Flow,
1952
929
                               _direction: u8,
1953
929
                               input: *const u8,
1954
929
                               len: u32,
1955
929
                               _rdir: *mut u8)
1956
929
    -> AppProto
1957
{
1958
929
    if input.is_null() {
1959
0
        return ALPROTO_UNKNOWN;
1960
929
    }
1961
929
    let slice: &[u8] = build_slice!(input, len as usize);
1962
929
    match nfs_probe_udp(slice, Direction::ToClient) {
1963
477
        1 => { ALPROTO_NFS },
1964
452
        -1 => { ALPROTO_FAILED },
1965
0
        _ => { ALPROTO_UNKNOWN },
1966
    }
1967
929
}
1968
1969
// Parser name as a C style string.
1970
const PARSER_NAME: &[u8] = b"nfs\0";
1971
1972
#[no_mangle]
1973
34
pub unsafe extern "C" fn rs_nfs_register_parser() {
1974
34
    let default_port = CString::new("[2049]").unwrap();
1975
34
    let parser = RustParser {
1976
34
        name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
1977
34
        default_port: std::ptr::null(),
1978
34
        ipproto: IPPROTO_TCP,
1979
34
        probe_ts: None,
1980
34
        probe_tc: None,
1981
34
        min_depth: 0,
1982
34
        max_depth: 16,
1983
34
        state_new: rs_nfs_state_new,
1984
34
        state_free: rs_nfs_state_free,
1985
34
        tx_free: rs_nfs_state_tx_free,
1986
34
        parse_ts: rs_nfs_parse_request,
1987
34
        parse_tc: rs_nfs_parse_response,
1988
34
        get_tx_count: rs_nfs_state_get_tx_count,
1989
34
        get_tx: rs_nfs_state_get_tx,
1990
34
        tx_comp_st_ts: 1,
1991
34
        tx_comp_st_tc: 1,
1992
34
        tx_get_progress: rs_nfs_tx_get_alstate_progress,
1993
34
        get_eventinfo: Some(NFSEvent::get_event_info),
1994
34
        get_eventinfo_byid : Some(NFSEvent::get_event_info_by_id),
1995
34
        localstorage_new: None,
1996
34
        localstorage_free: None,
1997
34
        get_tx_files: Some(rs_nfs_gettxfiles),
1998
34
        get_tx_iterator: Some(applayer::state_get_tx_iterator::<NFSState, NFSTransaction>),
1999
34
        get_tx_data: rs_nfs_get_tx_data,
2000
34
        get_state_data: rs_nfs_get_state_data,
2001
34
        apply_tx_config: None,
2002
34
        flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS,
2003
34
        truncate: None,
2004
34
        get_frame_id_by_name: Some(NFSFrameType::ffi_id_from_name),
2005
34
        get_frame_name_by_id: Some(NFSFrameType::ffi_name_from_id),
2006
34
    };
2007
2008
34
    let ip_proto_str = CString::new("tcp").unwrap();
2009
2010
34
    if AppLayerProtoDetectConfProtoDetectionEnabled(
2011
34
        ip_proto_str.as_ptr(),
2012
34
        parser.name,
2013
34
    ) != 0
2014
    {
2015
34
        let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
2016
34
        ALPROTO_NFS = alproto;
2017
2018
34
        let midstream = conf_get_bool("stream.midstream");
2019
34
        if midstream {
2020
33
            if AppLayerProtoDetectPPParseConfPorts(ip_proto_str.as_ptr(), IPPROTO_TCP,
2021
33
                    parser.name, ALPROTO_NFS, 0, NFS_MIN_FRAME_LEN,
2022
33
                    rs_nfs_probe_ms, rs_nfs_probe_ms) == 0 {
2023
33
                SCLogDebug!("No NFSTCP app-layer configuration, enabling NFSTCP detection TCP detection on port {:?}.",
2024
33
                            default_port);
2025
33
                /* register 'midstream' probing parsers if midstream is enabled. */
2026
33
                AppLayerProtoDetectPPRegister(IPPROTO_TCP,
2027
33
                    default_port.as_ptr(), ALPROTO_NFS, 0,
2028
33
                    NFS_MIN_FRAME_LEN, Direction::ToServer.into(),
2029
33
                    rs_nfs_probe_ms, rs_nfs_probe_ms);
2030
33
            }
2031
1
        } else {
2032
1
            AppLayerProtoDetectPPRegister(IPPROTO_TCP,
2033
1
                default_port.as_ptr(), ALPROTO_NFS, 0,
2034
1
                NFS_MIN_FRAME_LEN, Direction::ToServer.into(),
2035
1
                rs_nfs_probe, rs_nfs_probe);
2036
1
        }
2037
34
        if AppLayerParserConfParserEnabled(
2038
34
            ip_proto_str.as_ptr(),
2039
34
            parser.name,
2040
34
        ) != 0
2041
34
        {
2042
34
            let _ = AppLayerRegisterParser(&parser, alproto);
2043
34
        }
2044
        SCLogDebug!("Rust nfs parser registered.");
2045
34
        let retval = conf_get("app-layer.protocols.nfs.max-requests");
2046
34
        if let Some(val) = retval {
2047
0
            if let Ok(v) = val.parse::<usize>() {
2048
0
                if v > 0 {
2049
0
                    NFS_CFG_MAX_REQ = v;
2050
0
                } else {
2051
0
                    SCLogError!("Invalid max-requests value, must be >0");
2052
                }
2053
            } else {
2054
0
                SCLogError!("Invalid max-requests value");
2055
            }
2056
34
        }
2057
34
        let retval = conf_get("app-layer.protocols.nfs.max-names");
2058
34
        if let Some(val) = retval {
2059
0
            if let Ok(v) = val.parse::<usize>() {
2060
0
                if v > 0 {
2061
0
                    NFS_CFG_MAX_NAMES = v;
2062
0
                } else {
2063
0
                    SCLogError!("Invalid max-names value, must be >0");
2064
                }
2065
            } else {
2066
0
                SCLogError!("Invalid max-names value");
2067
            }
2068
34
        }
2069
0
    } else {
2070
0
        SCLogDebug!("Protocol detector and parser disabled for nfs.");
2071
0
    }
2072
34
}
2073
2074
#[no_mangle]
2075
34
pub unsafe extern "C" fn rs_nfs_udp_register_parser() {
2076
34
    let default_port = CString::new("[2049]").unwrap();
2077
34
    let parser = RustParser {
2078
34
        name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
2079
34
        default_port: std::ptr::null(),
2080
34
        ipproto: IPPROTO_UDP,
2081
34
        probe_ts: None,
2082
34
        probe_tc: None,
2083
34
        min_depth: 0,
2084
34
        max_depth: 16,
2085
34
        state_new: rs_nfs_state_new,
2086
34
        state_free: rs_nfs_state_free,
2087
34
        tx_free: rs_nfs_state_tx_free,
2088
34
        parse_ts: rs_nfs_parse_request_udp,
2089
34
        parse_tc: rs_nfs_parse_response_udp,
2090
34
        get_tx_count: rs_nfs_state_get_tx_count,
2091
34
        get_tx: rs_nfs_state_get_tx,
2092
34
        tx_comp_st_ts: 1,
2093
34
        tx_comp_st_tc: 1,
2094
34
        tx_get_progress: rs_nfs_tx_get_alstate_progress,
2095
34
        get_eventinfo: Some(NFSEvent::get_event_info),
2096
34
        get_eventinfo_byid : Some(NFSEvent::get_event_info_by_id),
2097
34
        localstorage_new: None,
2098
34
        localstorage_free: None,
2099
34
        get_tx_files: Some(rs_nfs_gettxfiles),
2100
34
        get_tx_iterator: Some(applayer::state_get_tx_iterator::<NFSState, NFSTransaction>),
2101
34
        get_tx_data: rs_nfs_get_tx_data,
2102
34
        get_state_data: rs_nfs_get_state_data,
2103
34
        apply_tx_config: None,
2104
34
        flags: 0,
2105
34
        truncate: None,
2106
34
        get_frame_id_by_name: Some(NFSFrameType::ffi_id_from_name),
2107
34
        get_frame_name_by_id: Some(NFSFrameType::ffi_name_from_id),
2108
34
    };
2109
2110
34
    let ip_proto_str = CString::new("udp").unwrap();
2111
2112
34
    if AppLayerProtoDetectConfProtoDetectionEnabled(
2113
34
        ip_proto_str.as_ptr(),
2114
34
        parser.name,
2115
34
    ) != 0
2116
    {
2117
34
        let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
2118
34
        ALPROTO_NFS = alproto;
2119
2120
34
        if AppLayerProtoDetectPPParseConfPorts(ip_proto_str.as_ptr(), IPPROTO_UDP,
2121
34
                parser.name, ALPROTO_NFS, 0, NFS_MIN_FRAME_LEN,
2122
34
                rs_nfs_probe_udp_ts, rs_nfs_probe_udp_tc) == 0 {
2123
34
            SCLogDebug!("No NFSUDP app-layer configuration, enabling NFSUDP detection UDP detection on port {:?}.",
2124
34
                        default_port);
2125
34
            AppLayerProtoDetectPPRegister(IPPROTO_UDP,
2126
34
                default_port.as_ptr(), ALPROTO_NFS, 0,
2127
34
                NFS_MIN_FRAME_LEN, Direction::ToServer.into(),
2128
34
                rs_nfs_probe_udp_ts, rs_nfs_probe_udp_tc);
2129
34
        }
2130
34
        if AppLayerParserConfParserEnabled(
2131
34
            ip_proto_str.as_ptr(),
2132
34
            parser.name,
2133
34
        ) != 0
2134
34
        {
2135
34
            let _ = AppLayerRegisterParser(&parser, alproto);
2136
34
        }
2137
34
        if let Some(val) = conf_get("app-layer.protocols.nfs.max-tx") {
2138
0
            if let Ok(v) = val.parse::<usize>() {
2139
0
                NFS_MAX_TX = v;
2140
0
            } else {
2141
0
                SCLogError!("Invalid value for nfs.max-tx");
2142
            }
2143
34
        }
2144
        SCLogDebug!("Rust nfs parser registered.");
2145
0
    } else {
2146
0
        SCLogDebug!("Protocol detector and parser disabled for nfs.");
2147
0
    }
2148
34
}