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