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