]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
nfs3: move nfs3 specific handling into own file
authorVictor Julien <victor@inliniac.net>
Sat, 21 Jul 2018 07:54:36 +0000 (09:54 +0200)
committerVictor Julien <victor@inliniac.net>
Mon, 23 Jul 2018 09:57:22 +0000 (11:57 +0200)
rust/src/nfs/mod.rs
rust/src/nfs/nfs.rs
rust/src/nfs/nfs3.rs [new file with mode: 0644]

index 5638b0c8cb7a8cdf5aa2991844490a6c31010199..b9922eee5d9a8d81f8d8b6614096a614429f120c 100644 (file)
@@ -21,8 +21,9 @@ pub mod nfs_records;
 pub mod nfs2_records;
 pub mod nfs3_records;
 pub mod nfs4_records;
-pub mod nfs4;
 pub mod nfs;
+pub mod nfs3;
+pub mod nfs4;
 pub mod log;
 
 //#[cfg(feature = "lua")]
index 19c1f7317dfe1d2ae764f7a8dcb7f41dca16b16e..7cbcf31f085acbc4d1e9ca0473e7a986e757a3c6 100644 (file)
@@ -40,14 +40,6 @@ use nfs::nfs_records::*;
 use nfs::nfs2_records::*;
 use nfs::nfs3_records::*;
 
-/// nom bug leads to this wrappers being necessary
-/// TODO for some reason putting these in parser.rs and making them public
-/// leads to a compile error wrt an unknown lifetime identifier 'a
-//named!(many0_nfs3_request_objects<Vec<Nfs3RequestObject<'a>>>, many0!(parse_nfs3_request_object));
-//named!(many0_nfs3_reply_objects<Vec<Nfs3ReplyObject<'a>>>, many0!(parse_nfs3_reply_object));
-named!(many0_nfs3_response_readdirplus_entries<Vec<Nfs3ResponseReaddirplusEntry<'a>>>,
-        many0!(parse_nfs3_response_readdirplus_entry_cond));
-
 pub static mut SURICATA_NFS3_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
 
 /*
@@ -455,7 +447,7 @@ impl NFSState {
     }
 
     // TODO maybe not enough users to justify a func
-    fn mark_response_tx_done(&mut self, xid: u32, rpc_status: u32, nfs_status: u32, resp_handle: &Vec<u8>)
+    pub fn mark_response_tx_done(&mut self, xid: u32, rpc_status: u32, nfs_status: u32, resp_handle: &Vec<u8>)
     {
         match self.get_tx_by_xid(xid) {
             Some(mytx) => {
@@ -475,7 +467,7 @@ impl NFSState {
         }
     }
 
-    fn process_request_record_lookup<'b>(&mut self, r: &RpcPacket<'b>, xidmap: &mut NFSRequestXidMap) {
+    pub fn process_request_record_lookup<'b>(&mut self, r: &RpcPacket<'b>, xidmap: &mut NFSRequestXidMap) {
         match parse_nfs3_request_lookup(r.prog_data) {
             IResult::Done(_, lookup) => {
                 SCLogDebug!("LOOKUP {:?}", lookup);
@@ -519,208 +511,6 @@ impl NFSState {
         }
     }
 
-    /// complete NFS3 request record
-    fn process_request_record_v3<'b>(&mut self, r: &RpcPacket<'b>) -> u32 {
-        SCLogDebug!("REQUEST {} procedure {} ({}) blob size {}",
-                r.hdr.xid, r.procedure, self.requestmap.len(), r.prog_data.len());
-
-        let mut xidmap = NFSRequestXidMap::new(r.progver, r.procedure, 0);
-        let mut aux_file_name = Vec::new();
-
-        if self.nfs_version == 0 {
-            self.nfs_version = r.progver as u16;
-        }
-
-        if r.procedure == NFSPROC3_LOOKUP {
-            self.process_request_record_lookup(r, &mut xidmap);
-
-        } else if r.procedure == NFSPROC3_ACCESS {
-            match parse_nfs3_request_access(r.prog_data) {
-                IResult::Done(_, ar) => {
-                    xidmap.file_handle = ar.handle.value.to_vec();
-                    self.xidmap_handle2name(&mut xidmap);
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-        } else if r.procedure == NFSPROC3_GETATTR {
-            match parse_nfs3_request_getattr(r.prog_data) {
-                IResult::Done(_, gar) => {
-                    xidmap.file_handle = gar.handle.value.to_vec();
-                    self.xidmap_handle2name(&mut xidmap);
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-        } else if r.procedure == NFSPROC3_READDIRPLUS {
-            match parse_nfs3_request_readdirplus(r.prog_data) {
-                IResult::Done(_, rdp) => {
-                    xidmap.file_handle = rdp.handle.value.to_vec();
-                    self.xidmap_handle2name(&mut xidmap);
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-        } else if r.procedure == NFSPROC3_READ {
-            match parse_nfs3_request_read(r.prog_data) {
-                IResult::Done(_, nfs3_read_record) => {
-                    xidmap.chunk_offset = nfs3_read_record.offset;
-                    xidmap.file_handle = nfs3_read_record.handle.value.to_vec();
-                    self.xidmap_handle2name(&mut xidmap);
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-        } else if r.procedure == NFSPROC3_WRITE {
-            match parse_nfs3_request_write(r.prog_data) {
-                IResult::Done(_, w) => {
-                    self.process_write_record(r, &w);
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            }
-        } else if r.procedure == NFSPROC3_CREATE {
-            match parse_nfs3_request_create(r.prog_data) {
-                IResult::Done(_, nfs3_create_record) => {
-                    xidmap.file_handle = nfs3_create_record.handle.value.to_vec();
-                    xidmap.file_name = nfs3_create_record.name_vec;
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-
-        } else if r.procedure == NFSPROC3_REMOVE {
-            match parse_nfs3_request_remove(r.prog_data) {
-                IResult::Done(_, rr) => {
-                    xidmap.file_handle = rr.handle.value.to_vec();
-                    xidmap.file_name = rr.name_vec;
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-
-        } else if r.procedure == NFSPROC3_RENAME {
-            match parse_nfs3_request_rename(r.prog_data) {
-                IResult::Done(_, rr) => {
-                    xidmap.file_handle = rr.from_handle.value.to_vec();
-                    xidmap.file_name = rr.from_name_vec;
-                    aux_file_name = rr.to_name_vec;
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-        } else if r.procedure == NFSPROC3_MKDIR {
-            match parse_nfs3_request_mkdir(r.prog_data) {
-                IResult::Done(_, mr) => {
-                    xidmap.file_handle = mr.handle.value.to_vec();
-                    xidmap.file_name = mr.name_vec;
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-        } else if r.procedure == NFSPROC3_RMDIR {
-            match parse_nfs3_request_rmdir(r.prog_data) {
-                IResult::Done(_, rr) => {
-                    xidmap.file_handle = rr.handle.value.to_vec();
-                    xidmap.file_name = rr.name_vec;
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-        } else if r.procedure == NFSPROC3_COMMIT {
-            SCLogDebug!("COMMIT, closing shop");
-
-            match parse_nfs3_request_commit(r.prog_data) {
-                IResult::Done(_, cr) => {
-                    let file_handle = cr.handle.value.to_vec();
-                    match self.get_file_tx_by_handle(&file_handle, STREAM_TOSERVER) {
-                        Some((tx, files, flags)) => {
-                            if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
-                                tdf.chunk_count += 1;
-                                tdf.file_additional_procs.push(NFSPROC3_COMMIT);
-                                tdf.file_tracker.close(files, flags);
-                                tdf.file_last_xid = r.hdr.xid;
-                                tx.is_last = true;
-                                tx.request_done = true;
-                            }
-                        },
-                        None => { },
-                    }
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-        }
-
-        if !(r.procedure == NFSPROC3_COMMIT || // commit handled separately
-             r.procedure == NFSPROC3_WRITE  || // write handled in file tx
-             r.procedure == NFSPROC3_READ)     // read handled in file tx at reply
-        {
-            let mut tx = self.new_tx();
-            tx.xid = r.hdr.xid;
-            tx.procedure = r.procedure;
-            tx.request_done = true;
-            tx.file_name = xidmap.file_name.to_vec();
-            tx.nfs_version = r.progver as u16;
-            tx.file_handle = xidmap.file_handle.to_vec();
-
-            if r.procedure == NFSPROC3_RENAME {
-                tx.type_data = Some(NFSTransactionTypeData::RENAME(aux_file_name));
-            }
-
-            tx.auth_type = r.creds_flavor;
-            match r.creds {
-                RpcRequestCreds::Unix(ref u) => {
-                    tx.request_machine_name = u.machine_name_buf.to_vec();
-                    tx.request_uid = u.uid;
-                    tx.request_gid = u.gid;
-                },
-                _ => { },
-            }
-            SCLogDebug!("TX created: ID {} XID {} PROCEDURE {}",
-                    tx.id, tx.xid, tx.procedure);
-            self.transactions.push(tx);
-
-        } else if r.procedure == NFSPROC3_READ {
-
-            let found = match self.get_file_tx_by_handle(&xidmap.file_handle, STREAM_TOCLIENT) {
-                Some((_, _, _)) => true,
-                None => false,
-            };
-            if !found {
-                let (tx, _, _) = self.new_file_tx(&xidmap.file_handle, &xidmap.file_name, STREAM_TOCLIENT);
-                tx.procedure = NFSPROC3_READ;
-                tx.xid = r.hdr.xid;
-                tx.is_first = true;
-                tx.nfs_version = r.progver as u16;
-
-                tx.auth_type = r.creds_flavor;
-                match r.creds {
-                    RpcRequestCreds::Unix(ref u) => {
-                        tx.request_machine_name = u.machine_name_buf.to_vec();
-                        tx.request_uid = u.uid;
-                        tx.request_gid = u.gid;
-                    },
-                    _ => { },
-                }
-            }
-        }
-
-        self.requestmap.insert(r.hdr.xid, xidmap);
-        0
-    }
-
     /// complete request record
     fn process_request_record_v2<'b>(&mut self, r: &RpcPacket<'b>) -> u32 {
         SCLogDebug!("NFSv2 REQUEST {} procedure {} ({}) blob size {}",
@@ -826,7 +616,7 @@ impl NFSState {
         return None;
     }
 
-    fn process_write_record<'b>(&mut self, r: &RpcPacket<'b>, w: &Nfs3RequestWrite<'b>) -> u32 {
+    pub fn process_write_record<'b>(&mut self, r: &RpcPacket<'b>, w: &Nfs3RequestWrite<'b>) -> u32 {
         // for now assume that stable FILE_SYNC flags means a single chunk
         let is_last = if w.stable == 2 { true } else { false };
 
@@ -902,117 +692,6 @@ impl NFSState {
         return self.process_write_record(r, w);
     }
 
-    fn process_reply_record_v3<'b>(&mut self, r: &RpcReplyPacket<'b>, xidmap: &mut NFSRequestXidMap) -> u32 {
-        let mut nfs_status = 0;
-        let mut resp_handle = Vec::new();
-
-        if xidmap.procedure == NFSPROC3_LOOKUP {
-            match parse_nfs3_response_lookup(r.prog_data) {
-                IResult::Done(_, lookup) => {
-                    SCLogDebug!("LOOKUP: {:?}", lookup);
-                    SCLogDebug!("RESPONSE LOOKUP file_name {:?}", xidmap.file_name);
-
-                    nfs_status = lookup.status;
-
-                    SCLogDebug!("LOOKUP handle {:?}", lookup.handle);
-                    self.namemap.insert(lookup.handle.value.to_vec(), xidmap.file_name.to_vec());
-                    resp_handle = lookup.handle.value.to_vec();
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-        } else if xidmap.procedure == NFSPROC3_CREATE {
-            match parse_nfs3_response_create(r.prog_data) {
-                IResult::Done(_, nfs3_create_record) => {
-                    SCLogDebug!("nfs3_create_record: {:?}", nfs3_create_record);
-
-                    SCLogDebug!("RESPONSE CREATE file_name {:?}", xidmap.file_name);
-                    nfs_status = nfs3_create_record.status;
-
-                    if let Some(h) = nfs3_create_record.handle {
-                        SCLogDebug!("handle {:?}", h);
-                        self.namemap.insert(h.value.to_vec(), xidmap.file_name.to_vec());
-                        resp_handle = h.value.to_vec();
-                    }
-
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            };
-        } else if xidmap.procedure == NFSPROC3_READ {
-            match parse_nfs3_reply_read(r.prog_data) {
-                IResult::Done(_, ref reply) => {
-                    self.process_read_record(r, reply, Some(&xidmap));
-                    nfs_status = reply.status;
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            }
-        } else if xidmap.procedure == NFSPROC3_READDIRPLUS {
-            match parse_nfs3_response_readdirplus(r.prog_data) {
-                IResult::Done(_, ref reply) => {
-                    //SCLogDebug!("READDIRPLUS reply {:?}", reply);
-
-                    nfs_status = reply.status;
-
-                    // cut off final eof field
-                    let d = &reply.data[..reply.data.len()-4 as usize];
-
-                    // store all handle/filename mappings
-                    match many0_nfs3_response_readdirplus_entries(d) {
-                        IResult::Done(_, ref entries) => {
-                            for ce in entries {
-                                SCLogDebug!("ce {:?}", ce);
-                                match ce.entry {
-                                    Some(ref e) => {
-                                        SCLogDebug!("e {:?}", e);
-                                        match e.handle {
-                                            Some(ref h) => {
-                                                SCLogDebug!("h {:?}", h);
-                                                self.namemap.insert(h.value.to_vec(), e.name_vec.to_vec());
-                                            },
-                                            _ => { },
-                                        }
-                                    },
-                                    _ => { },
-                                }
-                            }
-
-                            SCLogDebug!("READDIRPLUS ENTRIES reply {:?}", entries);
-                        },
-                        _ => {
-                            self.set_event(NFSEvent::MalformedData);
-                        },
-                    }
-                },
-                _ => {
-                    self.set_event(NFSEvent::MalformedData);
-                },
-            }
-        }
-        // for all other record types only parse the status
-        else {
-            let stat = match nom::be_u32(&r.prog_data) {
-                nom::IResult::Done(_, stat) => {
-                    stat as u32
-                }
-                _ => 0 as u32
-            };
-            nfs_status = stat;
-        }
-        SCLogDebug!("REPLY {} to procedure {} blob size {}",
-                r.hdr.xid, xidmap.procedure, r.prog_data.len());
-
-        if xidmap.procedure != NFSPROC3_READ {
-            self.mark_response_tx_done(r.hdr.xid, r.reply_state, nfs_status, &resp_handle);
-        }
-
-        0
-    }
-
     fn process_reply_record_v2<'b>(&mut self, r: &RpcReplyPacket<'b>, xidmap: &NFSRequestXidMap) -> u32 {
         let mut nfs_status = 0;
         let resp_handle = Vec::new();
diff --git a/rust/src/nfs/nfs3.rs b/rust/src/nfs/nfs3.rs
new file mode 100644 (file)
index 0000000..a76b0a3
--- /dev/null
@@ -0,0 +1,353 @@
+/* Copyright (C) 2017-2018 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+// written by Victor Julien
+
+use nom;
+use nom::IResult;
+use log::*;
+use core::*;
+
+use nfs::nfs::*;
+use nfs::types::*;
+use nfs::rpc_records::*;
+use nfs::nfs3_records::*;
+
+/// nom bug leads to this wrappers being necessary
+/// TODO for some reason putting these in parser.rs and making them public
+/// leads to a compile error wrt an unknown lifetime identifier 'a
+//named!(many0_nfs3_request_objects<Vec<Nfs3RequestObject<'a>>>, many0!(parse_nfs3_request_object));
+//named!(many0_nfs3_reply_objects<Vec<Nfs3ReplyObject<'a>>>, many0!(parse_nfs3_reply_object));
+named!(many0_nfs3_response_readdirplus_entries<Vec<Nfs3ResponseReaddirplusEntry<'a>>>,
+        many0!(parse_nfs3_response_readdirplus_entry_cond));
+
+impl NFSState {
+    /// complete NFS3 request record
+    pub fn process_request_record_v3<'b>(&mut self, r: &RpcPacket<'b>) -> u32 {
+        SCLogDebug!("REQUEST {} procedure {} ({}) blob size {}",
+                r.hdr.xid, r.procedure, self.requestmap.len(), r.prog_data.len());
+
+        let mut xidmap = NFSRequestXidMap::new(r.progver, r.procedure, 0);
+        let mut aux_file_name = Vec::new();
+
+        if self.nfs_version == 0 {
+            self.nfs_version = r.progver as u16;
+        }
+
+        if r.procedure == NFSPROC3_LOOKUP {
+            self.process_request_record_lookup(r, &mut xidmap);
+
+        } else if r.procedure == NFSPROC3_ACCESS {
+            match parse_nfs3_request_access(r.prog_data) {
+                IResult::Done(_, ar) => {
+                    xidmap.file_handle = ar.handle.value.to_vec();
+                    self.xidmap_handle2name(&mut xidmap);
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+        } else if r.procedure == NFSPROC3_GETATTR {
+            match parse_nfs3_request_getattr(r.prog_data) {
+                IResult::Done(_, gar) => {
+                    xidmap.file_handle = gar.handle.value.to_vec();
+                    self.xidmap_handle2name(&mut xidmap);
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+        } else if r.procedure == NFSPROC3_READDIRPLUS {
+            match parse_nfs3_request_readdirplus(r.prog_data) {
+                IResult::Done(_, rdp) => {
+                    xidmap.file_handle = rdp.handle.value.to_vec();
+                    self.xidmap_handle2name(&mut xidmap);
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+        } else if r.procedure == NFSPROC3_READ {
+            match parse_nfs3_request_read(r.prog_data) {
+                IResult::Done(_, nfs3_read_record) => {
+                    xidmap.chunk_offset = nfs3_read_record.offset;
+                    xidmap.file_handle = nfs3_read_record.handle.value.to_vec();
+                    self.xidmap_handle2name(&mut xidmap);
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+        } else if r.procedure == NFSPROC3_WRITE {
+            match parse_nfs3_request_write(r.prog_data) {
+                IResult::Done(_, w) => {
+                    self.process_write_record(r, &w);
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            }
+        } else if r.procedure == NFSPROC3_CREATE {
+            match parse_nfs3_request_create(r.prog_data) {
+                IResult::Done(_, nfs3_create_record) => {
+                    xidmap.file_handle = nfs3_create_record.handle.value.to_vec();
+                    xidmap.file_name = nfs3_create_record.name_vec;
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+
+        } else if r.procedure == NFSPROC3_REMOVE {
+            match parse_nfs3_request_remove(r.prog_data) {
+                IResult::Done(_, rr) => {
+                    xidmap.file_handle = rr.handle.value.to_vec();
+                    xidmap.file_name = rr.name_vec;
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+
+        } else if r.procedure == NFSPROC3_RENAME {
+            match parse_nfs3_request_rename(r.prog_data) {
+                IResult::Done(_, rr) => {
+                    xidmap.file_handle = rr.from_handle.value.to_vec();
+                    xidmap.file_name = rr.from_name_vec;
+                    aux_file_name = rr.to_name_vec;
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+        } else if r.procedure == NFSPROC3_MKDIR {
+            match parse_nfs3_request_mkdir(r.prog_data) {
+                IResult::Done(_, mr) => {
+                    xidmap.file_handle = mr.handle.value.to_vec();
+                    xidmap.file_name = mr.name_vec;
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+        } else if r.procedure == NFSPROC3_RMDIR {
+            match parse_nfs3_request_rmdir(r.prog_data) {
+                IResult::Done(_, rr) => {
+                    xidmap.file_handle = rr.handle.value.to_vec();
+                    xidmap.file_name = rr.name_vec;
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+        } else if r.procedure == NFSPROC3_COMMIT {
+            SCLogDebug!("COMMIT, closing shop");
+
+            match parse_nfs3_request_commit(r.prog_data) {
+                IResult::Done(_, cr) => {
+                    let file_handle = cr.handle.value.to_vec();
+                    match self.get_file_tx_by_handle(&file_handle, STREAM_TOSERVER) {
+                        Some((tx, files, flags)) => {
+                            if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
+                                tdf.chunk_count += 1;
+                                tdf.file_additional_procs.push(NFSPROC3_COMMIT);
+                                tdf.file_tracker.close(files, flags);
+                                tdf.file_last_xid = r.hdr.xid;
+                                tx.is_last = true;
+                                tx.request_done = true;
+                            }
+                        },
+                        None => { },
+                    }
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+        }
+
+        if !(r.procedure == NFSPROC3_COMMIT || // commit handled separately
+             r.procedure == NFSPROC3_WRITE  || // write handled in file tx
+             r.procedure == NFSPROC3_READ)     // read handled in file tx at reply
+        {
+            let mut tx = self.new_tx();
+            tx.xid = r.hdr.xid;
+            tx.procedure = r.procedure;
+            tx.request_done = true;
+            tx.file_name = xidmap.file_name.to_vec();
+            tx.nfs_version = r.progver as u16;
+            tx.file_handle = xidmap.file_handle.to_vec();
+
+            if r.procedure == NFSPROC3_RENAME {
+                tx.type_data = Some(NFSTransactionTypeData::RENAME(aux_file_name));
+            }
+
+            tx.auth_type = r.creds_flavor;
+            match r.creds {
+                RpcRequestCreds::Unix(ref u) => {
+                    tx.request_machine_name = u.machine_name_buf.to_vec();
+                    tx.request_uid = u.uid;
+                    tx.request_gid = u.gid;
+                },
+                _ => { },
+            }
+            SCLogDebug!("TX created: ID {} XID {} PROCEDURE {}",
+                    tx.id, tx.xid, tx.procedure);
+            self.transactions.push(tx);
+
+        } else if r.procedure == NFSPROC3_READ {
+
+            let found = match self.get_file_tx_by_handle(&xidmap.file_handle, STREAM_TOCLIENT) {
+                Some((_, _, _)) => true,
+                None => false,
+            };
+            if !found {
+                let (tx, _, _) = self.new_file_tx(&xidmap.file_handle, &xidmap.file_name, STREAM_TOCLIENT);
+                tx.procedure = NFSPROC3_READ;
+                tx.xid = r.hdr.xid;
+                tx.is_first = true;
+                tx.nfs_version = r.progver as u16;
+
+                tx.auth_type = r.creds_flavor;
+                match r.creds {
+                    RpcRequestCreds::Unix(ref u) => {
+                        tx.request_machine_name = u.machine_name_buf.to_vec();
+                        tx.request_uid = u.uid;
+                        tx.request_gid = u.gid;
+                    },
+                    _ => { },
+                }
+            }
+        }
+
+        self.requestmap.insert(r.hdr.xid, xidmap);
+        0
+    }
+
+    pub fn process_reply_record_v3<'b>(&mut self, r: &RpcReplyPacket<'b>, xidmap: &mut NFSRequestXidMap) -> u32 {
+        let mut nfs_status = 0;
+        let mut resp_handle = Vec::new();
+
+        if xidmap.procedure == NFSPROC3_LOOKUP {
+            match parse_nfs3_response_lookup(r.prog_data) {
+                IResult::Done(_, lookup) => {
+                    SCLogDebug!("LOOKUP: {:?}", lookup);
+                    SCLogDebug!("RESPONSE LOOKUP file_name {:?}", xidmap.file_name);
+
+                    nfs_status = lookup.status;
+
+                    SCLogDebug!("LOOKUP handle {:?}", lookup.handle);
+                    self.namemap.insert(lookup.handle.value.to_vec(), xidmap.file_name.to_vec());
+                    resp_handle = lookup.handle.value.to_vec();
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+        } else if xidmap.procedure == NFSPROC3_CREATE {
+            match parse_nfs3_response_create(r.prog_data) {
+                IResult::Done(_, nfs3_create_record) => {
+                    SCLogDebug!("nfs3_create_record: {:?}", nfs3_create_record);
+
+                    SCLogDebug!("RESPONSE CREATE file_name {:?}", xidmap.file_name);
+                    nfs_status = nfs3_create_record.status;
+
+                    if let Some(h) = nfs3_create_record.handle {
+                        SCLogDebug!("handle {:?}", h);
+                        self.namemap.insert(h.value.to_vec(), xidmap.file_name.to_vec());
+                        resp_handle = h.value.to_vec();
+                    }
+
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            };
+        } else if xidmap.procedure == NFSPROC3_READ {
+            match parse_nfs3_reply_read(r.prog_data) {
+                IResult::Done(_, ref reply) => {
+                    self.process_read_record(r, reply, Some(&xidmap));
+                    nfs_status = reply.status;
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            }
+        } else if xidmap.procedure == NFSPROC3_READDIRPLUS {
+            match parse_nfs3_response_readdirplus(r.prog_data) {
+                IResult::Done(_, ref reply) => {
+                    //SCLogDebug!("READDIRPLUS reply {:?}", reply);
+
+                    nfs_status = reply.status;
+
+                    // cut off final eof field
+                    let d = &reply.data[..reply.data.len()-4 as usize];
+
+                    // store all handle/filename mappings
+                    match many0_nfs3_response_readdirplus_entries(d) {
+                        IResult::Done(_, ref entries) => {
+                            for ce in entries {
+                                SCLogDebug!("ce {:?}", ce);
+                                match ce.entry {
+                                    Some(ref e) => {
+                                        SCLogDebug!("e {:?}", e);
+                                        match e.handle {
+                                            Some(ref h) => {
+                                                SCLogDebug!("h {:?}", h);
+                                                self.namemap.insert(h.value.to_vec(), e.name_vec.to_vec());
+                                            },
+                                            _ => { },
+                                        }
+                                    },
+                                    _ => { },
+                                }
+                            }
+
+                            SCLogDebug!("READDIRPLUS ENTRIES reply {:?}", entries);
+                        },
+                        _ => {
+                            self.set_event(NFSEvent::MalformedData);
+                        },
+                    }
+                },
+                _ => {
+                    self.set_event(NFSEvent::MalformedData);
+                },
+            }
+        }
+        // for all other record types only parse the status
+        else {
+            let stat = match nom::be_u32(&r.prog_data) {
+                nom::IResult::Done(_, stat) => {
+                    stat as u32
+                }
+                _ => 0 as u32
+            };
+            nfs_status = stat;
+        }
+        SCLogDebug!("REPLY {} to procedure {} blob size {}",
+                r.hdr.xid, xidmap.procedure, r.prog_data.len());
+
+        if xidmap.procedure != NFSPROC3_READ {
+            self.mark_response_tx_done(r.hdr.xid, r.reply_state, nfs_status, &resp_handle);
+        }
+
+        0
+    }
+
+}
+