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