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