nfs4: initial implementation

Implements record parsing and file extraction for READs and WRITEs.

Defines all types from RFC 7530.
pull/3335/head
Victor Julien 7 years ago
parent 75c5722b7e
commit 06f6c15954

@ -94,7 +94,12 @@ fn nfs_common_header(state: &NFSState, tx: &NFSTransaction) -> Json
{
let js = Json::object();
js.set_integer("version", state.nfs_version as u64);
js.set_string("procedure", &nfs3_procedure_string(tx.procedure));
let proc_string = if state.nfs_version < 4 {
nfs3_procedure_string(tx.procedure)
} else {
nfs4_procedure_string(tx.procedure)
};
js.set_string("procedure", &proc_string);
let file_name = String::from_utf8_lossy(&tx.file_name);
js.set_string("filename", &file_name);

@ -20,6 +20,8 @@ pub mod rpc_records;
pub mod nfs_records;
pub mod nfs2_records;
pub mod nfs3_records;
pub mod nfs4_records;
pub mod nfs4;
pub mod nfs;
pub mod log;

@ -155,8 +155,8 @@ pub struct NFSTransaction {
/// for state tracking. false means this side is in progress, true
/// that it's complete.
request_done: bool,
response_done: bool,
pub request_done: bool,
pub response_done: bool,
pub nfs_version: u16,
@ -231,13 +231,13 @@ impl Drop for NFSTransaction {
#[derive(Debug)]
pub struct NFSRequestXidMap {
progver: u32,
procedure: u32,
chunk_offset: u64,
file_name:Vec<u8>,
pub progver: u32,
pub procedure: u32,
pub chunk_offset: u64,
pub file_name:Vec<u8>,
/// READ replies can use this to get to the handle the request used
file_handle:Vec<u8>,
pub file_handle:Vec<u8>,
}
impl NFSRequestXidMap {
@ -285,7 +285,7 @@ impl NFSFiles {
}
/// little wrapper around the FileTransferTracker::new_chunk method
fn filetracker_newchunk(ft: &mut FileTransferTracker, files: &mut FileContainer,
pub fn filetracker_newchunk(ft: &mut FileTransferTracker, files: &mut FileContainer,
flags: u16, name: &Vec<u8>, data: &[u8],
chunk_offset: u64, chunk_size: u32, fill_bytes: u8, is_last: bool, xid: &u32)
{
@ -315,11 +315,11 @@ pub struct NFSState {
pub files: NFSFiles,
/// partial record tracking
ts_chunk_xid: u32,
tc_chunk_xid: u32,
pub ts_chunk_xid: u32,
pub tc_chunk_xid: u32,
/// size of the current chunk that we still need to receive
ts_chunk_left: u32,
tc_chunk_left: u32,
pub ts_chunk_left: u32,
pub tc_chunk_left: u32,
ts_ssn_gap: bool,
tc_ssn_gap: bool,
@ -483,7 +483,7 @@ impl NFSState {
};
}
fn xidmap_handle2name(&mut self, xidmap: &mut NFSRequestXidMap) {
pub fn xidmap_handle2name(&mut self, xidmap: &mut NFSRequestXidMap) {
match self.namemap.get(&xidmap.file_handle) {
Some(n) => {
SCLogDebug!("xidmap_handle2name: name {:?}", n);
@ -501,6 +501,10 @@ impl NFSState {
SCLogDebug!("REQUEST {} procedure {} ({}) blob size {}",
r.hdr.xid, r.procedure, self.requestmap.len(), r.prog_data.len());
if r.progver == 4 {
return self.process_request_record_v4(r);
}
let mut xidmap = NFSRequestXidMap::new(r.progver, r.procedure, 0);
let mut aux_file_name = Vec::new();
@ -779,7 +783,7 @@ impl NFSState {
0
}
fn new_file_tx(&mut self, file_handle: &Vec<u8>, file_name: &Vec<u8>, direction: u8)
pub fn new_file_tx(&mut self, file_handle: &Vec<u8>, file_name: &Vec<u8>, direction: u8)
-> (&mut NFSTransaction, &mut FileContainer, u16)
{
let mut tx = self.new_tx();
@ -803,7 +807,7 @@ impl NFSState {
return (tx_ref.unwrap(), files, flags)
}
fn get_file_tx_by_handle(&mut self, file_handle: &Vec<u8>, direction: u8)
pub fn get_file_tx_by_handle(&mut self, file_handle: &Vec<u8>, direction: u8)
-> Option<(&mut NFSTransaction, &mut FileContainer, u16)>
{
let fh = file_handle.to_vec();
@ -1080,9 +1084,8 @@ impl NFSState {
return self.process_reply_record_v3(r, &mut xidmap);
},
4 => {
SCLogDebug!("NFSv4 unsupported");
self.set_event(NFSEvent::UnsupportedVersion);
return 0;
SCLogDebug!("NFSv4 reply record");
return self.process_reply_record_v4(r, &mut xidmap);
},
_ => {
SCLogDebug!("Invalid NFS version");
@ -1199,7 +1202,7 @@ impl NFSState {
/// xidmapr is an Option as it's already removed from the map if we
/// have a complete record. Otherwise we do a lookup ourselves.
fn process_read_record<'b>(&mut self, r: &RpcReplyPacket<'b>,
pub fn process_read_record<'b>(&mut self, r: &RpcReplyPacket<'b>,
reply: &NfsReplyRead<'b>, xidmapr: Option<&NFSRequestXidMap>) -> u32
{
let file_name;
@ -1226,6 +1229,7 @@ impl NFSState {
}
},
}
SCLogDebug!("chunk_offset {}", chunk_offset);
let mut is_last = reply.eof;
let mut fill_bytes = 0;
@ -1233,7 +1237,8 @@ impl NFSState {
if pad != 0 {
fill_bytes = 4 - pad;
}
SCLogDebug!("XID {} fill_bytes {} reply.count {} reply.data_len {} reply.data.len() {}", r.hdr.xid, fill_bytes, reply.count, reply.data_len, reply.data.len());
SCLogDebug!("XID {} is_last {} fill_bytes {} reply.count {} reply.data_len {} reply.data.len() {}",
r.hdr.xid, is_last, fill_bytes, reply.count, reply.data_len, reply.data.len());
if nfs_version == 2 {
let size = match parse_nfs2_attribs(reply.attr_blob) {
@ -1256,6 +1261,7 @@ impl NFSState {
let found = match self.get_file_tx_by_handle(&file_handle, STREAM_TOCLIENT) {
Some((tx, files, flags)) => {
SCLogDebug!("updated TX {:?}", tx);
let ref mut tdf = match tx.type_data {
Some(NFSTransactionTypeData::FILE(ref mut x)) => x,
_ => { panic!("BUG") },
@ -1291,7 +1297,7 @@ impl NFSState {
filetracker_newchunk(&mut tdf.file_tracker, files, flags,
&file_name, reply.data, chunk_offset,
reply.count, fill_bytes as u8, is_last, &r.hdr.xid);
tx.procedure = NFSPROC3_READ;
tx.procedure = if nfs_version < 4 { NFSPROC3_READ } else { NFSPROC4_READ };
tx.xid = r.hdr.xid;
tx.is_first = true;
if is_last {
@ -2106,7 +2112,8 @@ pub fn nfs3_probe(i: &[u8], direction: u8) -> i8 {
match parse_rpc(i) {
IResult::Done(_, ref rpc) => {
if rpc.hdr.frag_len >= 40 && rpc.hdr.msgtype == 0 &&
rpc.rpcver == 2 && rpc.progver == 3 && rpc.program == 100003 &&
rpc.rpcver == 2 && (rpc.progver == 3 || rpc.progver == 4) &&
rpc.program == 100003 &&
rpc.procedure <= NFSPROC3_COMMIT
{
return 1;

@ -0,0 +1,250 @@
/* Copyright (C) 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
extern crate libc;
use nom::IResult;
use core::*;
use log::*;
use nfs::nfs::*;
use nfs::types::*;
use nfs::rpc_records::*;
use nfs::nfs_records::*;
use nfs::nfs4_records::*;
impl NFSState {
/* normal write: PUTFH (file handle), WRITE (write opts/data). File handle
* is not part of the write record itself so we pass it in here. */
fn write_v4<'b>(&mut self, r: &RpcPacket<'b>, w: &Nfs4RequestWrite<'b>, fh: &'b[u8])
{
// for now assume that stable FILE_SYNC flags means a single chunk
let is_last = if w.stable == 2 { true } else { false };
SCLogDebug!("is_last {}", is_last);
let mut fill_bytes = 0;
let pad = w.write_len % 4;
if pad != 0 {
fill_bytes = 4 - pad;
}
let file_handle = fh.to_vec();
let file_name = match self.namemap.get(fh) {
Some(n) => {
SCLogDebug!("WRITE name {:?}", n);
n.to_vec()
},
None => {
SCLogDebug!("WRITE object {:?} not found", w.stateid.data);
Vec::new()
},
};
let found = 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 {
filetracker_newchunk(&mut tdf.file_tracker, files, flags,
&file_name, w.data, w.offset,
w.write_len, fill_bytes as u8, is_last, &r.hdr.xid);
tdf.chunk_count += 1;
if is_last {
tdf.file_last_xid = r.hdr.xid;
tx.is_last = true;
tx.response_done = true;
}
}
true
},
None => { false },
};
if !found {
let (tx, files, flags) = self.new_file_tx(&file_handle, &file_name, STREAM_TOSERVER);
if let Some(NFSTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
filetracker_newchunk(&mut tdf.file_tracker, files, flags,
&file_name, w.data, w.offset,
w.write_len, fill_bytes as u8, is_last, &r.hdr.xid);
tx.procedure = NFSPROC4_WRITE;
tx.xid = r.hdr.xid;
tx.is_first = true;
tx.nfs_version = r.progver as u16;
if is_last {
tdf.file_last_xid = r.hdr.xid;
tx.is_last = true;
tx.request_done = true;
}
}
}
self.ts_chunk_xid = r.hdr.xid;
let file_data_len = w.data.len() as u32 - fill_bytes as u32;
self.ts_chunk_left = w.write_len as u32 - file_data_len as u32;
}
/* A normal READ request looks like: PUTFH (file handle) READ (read opts).
* We need the file handle for the READ.
*/
fn compound_request<'b>(&mut self, r: &RpcPacket<'b>,
cr: &Nfs4RequestCompoundRecord<'b>,
xidmap: &mut NFSRequestXidMap)
{
let mut last_putfh : Option<&'b[u8]> = None;
for c in &cr.commands {
SCLogDebug!("c {:?}", c);
match c {
&Nfs4RequestContent::PutFH(ref rd) => {
last_putfh = Some(rd.value);
}
&Nfs4RequestContent::Read(ref rd) => {
SCLogDebug!("READv4: {:?}", rd);
if let Some(fh) = last_putfh {
xidmap.chunk_offset = rd.offset;
xidmap.file_handle = fh.to_vec();
self.xidmap_handle2name(xidmap);
}
}
&Nfs4RequestContent::Open(ref rd) => {
SCLogDebug!("OPENv4: {}", String::from_utf8_lossy(&rd.filename));
xidmap.file_name = rd.filename.to_vec();
}
&Nfs4RequestContent::Lookup(ref rd) => {
SCLogDebug!("LOOKUPv4: {}", String::from_utf8_lossy(&rd.filename));
xidmap.file_name = rd.filename.to_vec();
}
&Nfs4RequestContent::Write(ref rd) => {
SCLogDebug!("WRITEv4: {:?}", rd);
if let Some(fh) = last_putfh {
self.write_v4(r, rd, fh);
}
}
&Nfs4RequestContent::Close(ref rd) => {
SCLogDebug!("CLOSEv4: {:?}", rd);
}
&Nfs4RequestContent::SetClientId(ref rd) => {
SCLogDebug!("SETCLIENTIDv4: client id {} r_netid {} r_addr {}",
String::from_utf8_lossy(&rd.client_id),
String::from_utf8_lossy(&rd.r_netid),
String::from_utf8_lossy(&rd.r_addr));
}
&_ => { },
}
}
}
/// complete request record
pub fn process_request_record_v4<'b>(&mut self, r: &RpcPacket<'b>) -> u32 {
SCLogDebug!("NFSv4 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);
if r.procedure == NFSPROC4_COMPOUND {
match parse_nfs4_request_compound(r.prog_data) {
IResult::Done(_, rd) => {
SCLogDebug!("NFSPROC4_COMPOUND: {:?}", rd);
self.compound_request(&r, &rd, &mut xidmap);
},
IResult::Incomplete(_n) => {
SCLogNotice!("NFSPROC4_COMPOUND: INCOMPLETE {:?}", _n);
self.set_event(NFSEvent::MalformedData);
},
IResult::Error(e) => { panic!("Parsing failed: {:?}",e); },
};
}
self.requestmap.insert(r.hdr.xid, xidmap);
0
}
fn compound_response<'b>(&mut self, r: &RpcReplyPacket<'b>,
cr: &Nfs4ResponseCompoundRecord<'b>,
xidmap: &mut NFSRequestXidMap)
{
let mut insert_filename_with_getfh = false;
for c in &cr.commands {
SCLogDebug!("c {:?}", c);
match c {
&Nfs4ResponseContent::ReadDir(s, ref rd) => {
if let &Some(ref rd) = rd {
SCLogDebug!("READDIRv4: status {} eof {}", s, rd.eof);
for d in &rd.listing {
if let &Some(ref d) = d {
SCLogDebug!("READDIRv4: dir {}", String::from_utf8_lossy(&d.name));
}
}
}
}
&Nfs4ResponseContent::Remove(s) => {
SCLogDebug!("REMOVE4: status {}", s);
},
&Nfs4ResponseContent::Read(s, ref rd) => {
if let &Some(ref rd) = rd {
SCLogDebug!("READ4: xidmap {:?} status {} data {}", xidmap, s, rd.data.len());
// convert record to generic read reply
let reply = NfsReplyRead {
status: s,
attr_follows: 0,
attr_blob: &[],
count: rd.count,
eof: rd.eof,
data_len: rd.data.len() as u32,
data: rd.data,
};
self.process_read_record(r, &reply, Some(&xidmap));
}
},
&Nfs4ResponseContent::Open(s, ref rd) => {
if let &Some(ref rd) = rd {
SCLogDebug!("OPENv4: status {} opendata {:?}", s, rd);
insert_filename_with_getfh = true;
}
},
&Nfs4ResponseContent::GetFH(_s, ref rd) => {
if let &Some(ref rd) = rd {
if insert_filename_with_getfh {
self.namemap.insert(rd.value.to_vec(),
xidmap.file_name.to_vec());
}
}
},
&_ => { },
}
}
}
pub fn process_reply_record_v4<'b>(&mut self, r: &RpcReplyPacket<'b>,
xidmap: &mut NFSRequestXidMap) -> u32 {
if xidmap.procedure == NFSPROC4_COMPOUND {
match parse_nfs4_response_compound(r.prog_data) {
IResult::Done(_, rd) => {
SCLogDebug!("COMPOUNDv4: {:?}", rd);
self.compound_response(&r, &rd, xidmap);
},
IResult::Incomplete(_) => {
self.set_event(NFSEvent::MalformedData);
},
IResult::Error(e) => { panic!("Parsing failed: {:?}",e); },
};
}
0
}
}

@ -0,0 +1,843 @@
/* Copyright (C) 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.
*/
//! Nom parsers for NFSv4 records
use nom::{be_u32, be_u64};
use nfs::types::*;
#[derive(Debug,PartialEq)]
pub enum Nfs4RequestContent<'a> {
PutFH(Nfs4Handle<'a>),
GetFH,
SaveFH,
PutRootFH,
ReadDir,
Open(Nfs4RequestOpen<'a>),
Lookup(Nfs4RequestLookup<'a>),
Read(Nfs4RequestRead<'a>),
Write(Nfs4RequestWrite<'a>),
Close(Nfs4StateId<'a>),
Rename(Nfs4RequestRename<'a>),
Create(Nfs4RequestCreate<'a>),
OpenConfirm(Nfs4RequestOpenConfirm<'a>),
Access(u32),
GetAttr(Nfs4Attr),
SetAttr(Nfs4RequestSetAttr<'a>),
Renew(u64),
Remove(&'a[u8]),
DelegReturn(Nfs4StateId<'a>),
SetClientId(Nfs4RequestSetClientId<'a>),
SetClientIdConfirm,
ExchangeId(Nfs4RequestExchangeId<'a>),
}
#[derive(Debug,PartialEq)]
pub struct Nfs4Attr {
attr_mask: u64,
}
named!(nfs4_parse_attr_fields<u32>,
do_parse!(
len: be_u32
>> take!(len)
>> (len)
));
named!(nfs4_parse_attrs<Nfs4Attr>,
do_parse!(
attr_cnt: be_u32
>> attr_mask1: be_u32
>> attr_mask2: cond!(attr_cnt == 2, be_u32)
>> nfs4_parse_attr_fields
>> ( Nfs4Attr {
attr_mask: ((attr_mask1 as u64) << 32) | attr_mask2.unwrap_or(0) as u64,
} )
));
named!(nfs4_parse_attrbits<Nfs4Attr>,
do_parse!(
attr_cnt: be_u32
>> attr_mask1: be_u32
>> attr_mask2: cond!(attr_cnt == 2, be_u32)
>> ( Nfs4Attr {
attr_mask: ((attr_mask1 as u64) << 32) | attr_mask2.unwrap_or(0) as u64,
} )
));
#[derive(Debug,PartialEq)]
pub struct Nfs4StateId<'a> {
pub seqid: u32,
pub data: &'a[u8],
}
named!(nfs4_parse_stateid<Nfs4StateId>,
do_parse!(
seqid: be_u32
>> data: take!(12)
>> ( Nfs4StateId {
seqid: seqid,
data: data,
})
)
);
#[derive(Debug,PartialEq)]
pub struct Nfs4Handle<'a> {
pub len: u32,
pub value: &'a[u8],
}
named!(nfs4_parse_handle<Nfs4Handle>,
do_parse!(
obj_len: be_u32
>> obj: take!(obj_len)
>> ( Nfs4Handle {
len: obj_len,
value: obj,
})
));
named!(nfs4_parse_nfsstring<&[u8]>,
do_parse!(
len: be_u32
>> data: take!(len)
>> _fill_bytes: cond!(len % 4 != 0, take!(4 - len % 4))
>> ( data )
));
named!(nfs4_req_putfh<Nfs4RequestContent>,
do_parse!(
h: nfs4_parse_handle
>> ( Nfs4RequestContent::PutFH(h) )
));
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestSetClientId<'a> {
pub client_id: &'a[u8],
pub r_netid: &'a[u8],
pub r_addr: &'a[u8],
}
named!(nfs4_req_setclientid<Nfs4RequestContent>,
do_parse!(
client_verifier: take!(8)
>> client_id: nfs4_parse_nfsstring
>> cb_program: be_u32
>> r_netid: nfs4_parse_nfsstring
>> r_addr: nfs4_parse_nfsstring
>> cb_id: be_u32
>> (Nfs4RequestContent::SetClientId(Nfs4RequestSetClientId {
client_id: client_id,
r_netid: r_netid,
r_addr: r_addr,
}))
));
named!(nfs4_req_setclientid_confirm<Nfs4RequestContent>,
do_parse!(
client_id: take!(8)
>> verifier: take!(8)
>> (Nfs4RequestContent::SetClientIdConfirm)
));
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestCreate<'a> {
pub ftype4: u32,
pub filename: &'a[u8],
}
named!(nfs4_req_create<Nfs4RequestContent>,
do_parse!(
ftype4: be_u32
>> filename: nfs4_parse_nfsstring
>> attrs: nfs4_parse_attrs
>> ( Nfs4RequestContent::Create(Nfs4RequestCreate {
ftype4: ftype4,
filename: filename,
})
))
);
#[derive(Debug,PartialEq)]
pub enum Nfs4OpenRequestContent<'a> {
Exclusive4(&'a[u8]),
Unchecked4(Nfs4Attr),
Guarded4(Nfs4Attr),
}
named!(nfs4_req_open_unchecked4<Nfs4OpenRequestContent>,
do_parse!(
attrs: nfs4_parse_attrs
>> ( Nfs4OpenRequestContent::Unchecked4(attrs) )
));
named!(nfs4_req_open_guarded4<Nfs4OpenRequestContent>,
do_parse!(
attrs: nfs4_parse_attrs
>> ( Nfs4OpenRequestContent::Guarded4(attrs) )
));
named!(nfs4_req_open_exclusive4<Nfs4OpenRequestContent>,
do_parse!(
ver: take!(8)
>> ( Nfs4OpenRequestContent::Exclusive4(ver) )
));
named!(nfs4_req_open_type<Nfs4OpenRequestContent>,
do_parse!(
mode: be_u32
>> data: switch!(value!(mode),
0 => call!(nfs4_req_open_unchecked4) |
1 => call!(nfs4_req_open_guarded4) |
2 => call!(nfs4_req_open_exclusive4))
>> ( data )
));
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestOpen<'a> {
pub open_type: u32,
pub filename: &'a[u8],
pub open_data: Option<Nfs4OpenRequestContent<'a>>,
}
named!(nfs4_req_open<Nfs4RequestContent>,
do_parse!(
seqid: be_u32
>> share_access: be_u32
>> share_deny: be_u32
>> client_id: be_u64
>> owner_len: be_u32
>> cond!(owner_len > 0, take!(owner_len))
>> open_type: be_u32
>> open_data: cond!(open_type == 1, nfs4_req_open_type)
>> claim_type: be_u32
>> filename: nfs4_parse_nfsstring
>> ( Nfs4RequestContent::Open(Nfs4RequestOpen {
open_type: open_type,
filename: filename,
open_data: open_data,
})
))
);
named!(nfs4_req_readdir<Nfs4RequestContent>,
do_parse!(
cookie: be_u64
>> cookie_verf: be_u64
>> dir_cnt: be_u32
>> max_cnt: be_u32
>> attr: nfs4_parse_attrbits
>> ( Nfs4RequestContent::ReadDir )
)
);
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestRename<'a> {
pub oldname: &'a[u8],
pub newname: &'a[u8],
}
named!(nfs4_req_rename<Nfs4RequestContent>,
do_parse!(
oldname: nfs4_parse_nfsstring
>> newname: nfs4_parse_nfsstring
>> ( Nfs4RequestContent::Rename(Nfs4RequestRename {
oldname: oldname,
newname: newname,
})
))
);
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestLookup<'a> {
pub filename: &'a[u8],
}
named!(nfs4_req_lookup<Nfs4RequestContent>,
do_parse!(
filename: nfs4_parse_nfsstring
>> ( Nfs4RequestContent::Lookup(Nfs4RequestLookup {
filename: filename,
})
))
);
named!(nfs4_req_remove<Nfs4RequestContent>,
do_parse!(
filename: nfs4_parse_nfsstring
>> ( Nfs4RequestContent::Remove(filename) )
));
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestSetAttr<'a> {
pub stateid: Nfs4StateId<'a>,
}
named!(nfs4_req_setattr<Nfs4RequestContent>,
do_parse!(
stateid: nfs4_parse_stateid
>> attrs: nfs4_parse_attrs
>> (Nfs4RequestContent::SetAttr(Nfs4RequestSetAttr {
stateid: stateid,
}))
));
named!(nfs4_req_getattr<Nfs4RequestContent>,
do_parse!(
attrs: nfs4_parse_attrbits
>> ( Nfs4RequestContent::GetAttr(attrs) )
)
);
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestWrite<'a> {
pub stateid: Nfs4StateId<'a>,
pub offset: u64,
pub stable: u32,
pub write_len: u32,
pub data: &'a[u8],
}
named!(nfs4_req_write<Nfs4RequestContent>,
do_parse!(
stateid: nfs4_parse_stateid
>> offset: be_u64
>> stable: be_u32
>> write_len: be_u32
>> data: take!(write_len)
>> _padding: cond!(write_len % 4 != 0, take!(4 - write_len % 4))
>> (Nfs4RequestContent::Write(Nfs4RequestWrite {
stateid: stateid,
offset: offset,
stable: stable,
write_len: write_len,
data: data,
}))
));
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestRead<'a> {
pub stateid: Nfs4StateId<'a>,
pub offset: u64,
pub count: u32,
}
named!(nfs4_req_read<Nfs4RequestContent>,
do_parse!(
stateid: nfs4_parse_stateid
>> offset: be_u64
>> count: be_u32
>> ( Nfs4RequestContent::Read(Nfs4RequestRead {
stateid: stateid,
offset: offset,
count: count,
})
))
);
named!(nfs4_req_close<Nfs4RequestContent>,
do_parse!(
seqid: be_u32
>> stateid: nfs4_parse_stateid
>> ( Nfs4RequestContent::Close(stateid) )
));
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestOpenConfirm<'a> {
pub stateid: Nfs4StateId<'a>,
}
named!(nfs4_req_open_confirm<Nfs4RequestContent>,
do_parse!(
stateid: nfs4_parse_stateid
>> seqid: be_u32
>> ( Nfs4RequestContent::OpenConfirm(Nfs4RequestOpenConfirm {
stateid: stateid,
})
))
);
named!(nfs4_req_delegreturn<Nfs4RequestContent>,
do_parse!(
a: nfs4_parse_stateid
>> ( Nfs4RequestContent::DelegReturn(a) )
)
);
named!(nfs4_req_renew<Nfs4RequestContent>,
do_parse!(
a: be_u64
>> ( Nfs4RequestContent::Renew(a) )
)
);
named!(nfs4_req_getfh<Nfs4RequestContent>,
do_parse!( ( Nfs4RequestContent::GetFH ) ));
named!(nfs4_req_savefh<Nfs4RequestContent>,
do_parse!( ( Nfs4RequestContent::SaveFH ) ));
named!(nfs4_req_putrootfh<Nfs4RequestContent>,
do_parse!( ( Nfs4RequestContent::PutRootFH ) ));
named!(nfs4_req_access<Nfs4RequestContent>,
do_parse!(
a: be_u32
>> ( Nfs4RequestContent::Access(a) )
)
);
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestExchangeId<'a> {
pub client_string: &'a[u8],
pub nii_domain: &'a[u8],
pub nii_name: &'a[u8],
}
named!(nfs4_req_exchangeid<Nfs4RequestContent>,
do_parse!(
verifier: take!(8)
>> eia_clientstring: nfs4_parse_nfsstring
>> eia_clientflags: be_u32
>> eia_state_protect: be_u32
>> eia_client_impl_id: be_u32
>> nii_domain: nfs4_parse_nfsstring
>> nii_name: nfs4_parse_nfsstring
>> nii_data_sec: be_u64
>> nii_data_nsec: be_u32
>> (Nfs4RequestContent::ExchangeId(
Nfs4RequestExchangeId {
client_string: eia_clientstring,
nii_domain: nii_domain,
nii_name: nii_name,
}
))
));
named!(parse_request_compound_command<Nfs4RequestContent>,
do_parse!(
cmd: be_u32
>> cmd_data: switch!(value!(cmd),
NFSPROC4_PUTFH => call!(nfs4_req_putfh) |
NFSPROC4_READ => call!(nfs4_req_read) |
NFSPROC4_WRITE => call!(nfs4_req_write) |
NFSPROC4_GETFH => call!(nfs4_req_getfh) |
NFSPROC4_SAVEFH => call!(nfs4_req_savefh) |
NFSPROC4_OPEN => call!(nfs4_req_open) |
NFSPROC4_CLOSE => call!(nfs4_req_close) |
NFSPROC4_LOOKUP => call!(nfs4_req_lookup) |
NFSPROC4_ACCESS => call!(nfs4_req_access) |
NFSPROC4_GETATTR => call!(nfs4_req_getattr) |
NFSPROC4_READDIR => call!(nfs4_req_readdir) |
NFSPROC4_RENEW => call!(nfs4_req_renew) |
NFSPROC4_OPEN_CONFIRM => call!(nfs4_req_open_confirm) |
NFSPROC4_REMOVE => call!(nfs4_req_remove) |
NFSPROC4_RENAME => call!(nfs4_req_rename) |
NFSPROC4_CREATE => call!(nfs4_req_create) |
NFSPROC4_DELEGRETURN => call!(nfs4_req_delegreturn) |
NFSPROC4_SETATTR => call!(nfs4_req_setattr) |
NFSPROC4_PUTROOTFH => call!(nfs4_req_putrootfh) |
NFSPROC4_SETCLIENTID => call!(nfs4_req_setclientid) |
NFSPROC4_SETCLIENTID_CONFIRM => call!(nfs4_req_setclientid_confirm) |
NFSPROC4_EXCHANGE_ID => call!(nfs4_req_exchangeid)
)
>> ( cmd_data )
));
#[derive(Debug,PartialEq)]
pub struct Nfs4RequestCompoundRecord<'a> {
pub commands: Vec<Nfs4RequestContent<'a>>,
}
named!(pub parse_nfs4_request_compound<Nfs4RequestCompoundRecord>,
do_parse!(
tag_len: be_u32
>> tag: cond!(tag_len > 0, take!(tag_len))
>> min_ver: be_u32
>> ops_cnt: be_u32
>> commands: count!(parse_request_compound_command, ops_cnt as usize)
>> (Nfs4RequestCompoundRecord {
commands: commands,
})
));
#[derive(Debug,PartialEq)]
pub enum Nfs4ResponseContent<'a> {
PutFH(u32),
PutRootFH(u32),
GetFH(u32, Option<Nfs4Handle<'a>>),
Lookup(u32),
SaveFH(u32),
Rename(u32),
Write(u32, Option<Nfs4ResponseWrite>),
Read(u32, Option<Nfs4ResponseRead<'a>>),
Renew(u32),
Open(u32, Option<Nfs4ResponseOpen<'a>>),
OpenConfirm(u32, Option<Nfs4StateId<'a>>),
Close(u32, Option<Nfs4StateId<'a>>),
GetAttr(u32, Option<Nfs4Attr>),
SetAttr(u32),
Access(u32, Option<Nfs4ResponseAccess>),
ReadDir(u32, Option<Nfs4ResponseReaddir<'a>>),
Remove(u32),
DelegReturn(u32),
SetClientId(u32),
SetClientIdConfirm(u32),
Create(u32),
}
#[derive(Debug,PartialEq)]
pub struct Nfs4ResponseWrite {
pub count: u32,
pub committed: u32,
}
named!(nfs4_res_write_ok<Nfs4ResponseWrite>,
do_parse!(
count: be_u32
>> committed: be_u32
>> verifier: be_u64
>> (Nfs4ResponseWrite {
count: count,
committed: committed,
})
));
named!(nfs4_res_write<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> wd: cond!(status == 0, nfs4_res_write_ok)
>> (Nfs4ResponseContent::Write(status, wd) )
));
#[derive(Debug,PartialEq)]
pub struct Nfs4ResponseRead<'a> {
pub eof: bool,
pub count: u32,
pub data: &'a[u8],
}
named!(nfs4_res_read_ok<Nfs4ResponseRead>,
do_parse!(
eof: be_u32
>> read_len: be_u32
>> read_data: take!(read_len)
>> (Nfs4ResponseRead {
eof: eof==1,
count: read_len,
data: read_data,
})
));
named!(nfs4_res_read<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> rd: cond!(status == 0, nfs4_res_read_ok)
>> (Nfs4ResponseContent::Read(status, rd) )
));
#[derive(Debug,PartialEq)]
pub struct Nfs4ResponseOpen<'a> {
pub stateid: Nfs4StateId<'a>,
pub result_flags: u32,
pub delegation_type: u32,
pub delegate_read: Option<Nfs4ResponseOpenDelegateRead<'a>>,
}
#[derive(Debug,PartialEq)]
pub struct Nfs4ResponseOpenDelegateRead<'a> {
pub stateid: Nfs4StateId<'a>,
}
named!(nfs4_res_open_ok_delegate_read<Nfs4ResponseOpenDelegateRead>,
do_parse!(
stateid: nfs4_parse_stateid
>> recall: be_u32
>> ace_type: be_u32
>> ace_flags: be_u32
>> ace_mask: be_u32
>> who_len: be_u32
>> who: take!(who_len)
>> (Nfs4ResponseOpenDelegateRead {
stateid: stateid,
})
));
named!(nfs4_res_open_ok<Nfs4ResponseOpen>,
do_parse!(
stateid: nfs4_parse_stateid
>> change_info: take!(20)
>> result_flags: be_u32
>> attrs: nfs4_parse_attrbits
>> delegation_type: be_u32
>> delegate_read: cond!(delegation_type == 1, nfs4_res_open_ok_delegate_read)
>> ( Nfs4ResponseOpen {
stateid: stateid, result_flags: result_flags,
delegation_type: delegation_type,
delegate_read: delegate_read,
} )
));
named!(nfs4_res_open<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> open_data: cond!(status == 0, nfs4_res_open_ok)
>> ( Nfs4ResponseContent::Open(status, open_data) )
));
#[derive(Debug,PartialEq)]
pub struct Nfs4ResponseReaddirEntry<'a> {
pub name: &'a[u8],
}
#[derive(Debug,PartialEq)]
pub struct Nfs4ResponseReaddir<'a> {
pub eof: bool,
pub listing: Vec<Option<Nfs4ResponseReaddirEntry<'a>>>,
}
named!(nfs4_res_readdir_entry_do<Nfs4ResponseReaddirEntry>,
do_parse!(
cookie: be_u64
>> name: nfs4_parse_nfsstring
>> attrs: nfs4_parse_attrs
>> ( Nfs4ResponseReaddirEntry {
name: name,
})
));
named!(nfs4_res_readdir_entry<Option<Nfs4ResponseReaddirEntry>>,
do_parse!(
value_follows: be_u32
>> entry: cond!(value_follows == 1, nfs4_res_readdir_entry_do)
>> (entry)
));
named!(nfs4_res_readdir_ok<Nfs4ResponseReaddir>,
do_parse!(
verifier: be_u64
// run parser until we find a 'value follows == 0'
>> listing: many_till!(call!(nfs4_res_readdir_entry), peek!(tag!(b"\x00\x00\x00\x00")))
// value follows == 0 checked by line above
>> _value_follows: be_u32
>> eof: be_u32
>> ( Nfs4ResponseReaddir { eof: eof==1, listing: listing.0 })
));
named!(nfs4_res_readdir<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> rd: cond!(status == 0, nfs4_res_readdir_ok)
>> ( Nfs4ResponseContent::ReadDir(status, rd) )
));
named!(nfs4_res_create_ok<Nfs4Attr>,
do_parse!(
change_info: take!(20)
>> attrs: nfs4_parse_attrbits
>> ( attrs )
));
named!(nfs4_res_create<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> attrs: cond!(status == 0, nfs4_res_create_ok)
>> ( Nfs4ResponseContent::Create(status) )
));
named!(nfs4_res_setattr_ok<Nfs4Attr>,
do_parse!(
attrs: nfs4_parse_attrbits
>> ( attrs )
));
named!(nfs4_res_setattr<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> attrs: cond!(status == 0, nfs4_res_setattr_ok)
>> ( Nfs4ResponseContent::SetAttr(status) )
));
named!(nfs4_res_getattr_ok<Nfs4Attr>,
do_parse!(
attrs: nfs4_parse_attrs
>> ( attrs )
));
named!(nfs4_res_getattr<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> attrs: cond!(status == 0, nfs4_res_getattr_ok)
>> ( Nfs4ResponseContent::GetAttr(status, attrs) )
));
named!(nfs4_res_openconfirm<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> stateid: cond!(status == 0, nfs4_parse_stateid)
>> ( Nfs4ResponseContent::OpenConfirm(status, stateid) )
));
named!(nfs4_res_close<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> stateid: cond!(status == 0, nfs4_parse_stateid)
>> ( Nfs4ResponseContent::Close(status, stateid) )
));
named!(nfs4_res_remove<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> cond!(status == 0, take!(20)) // change_info
>> ( Nfs4ResponseContent::Remove(status) )
));
named!(nfs4_res_rename<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> ( Nfs4ResponseContent::Rename(status) )
));
named!(nfs4_res_savefh<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> ( Nfs4ResponseContent::SaveFH(status) )
));
named!(nfs4_res_lookup<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> ( Nfs4ResponseContent::Lookup(status) )
));
named!(nfs4_res_renew<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> ( Nfs4ResponseContent::Renew(status) )
));
named!(nfs4_res_getfh<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> fh: cond!(status == 0, nfs4_parse_handle)
>> ( Nfs4ResponseContent::GetFH(status, fh) )
));
named!(nfs4_res_putfh<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> ( Nfs4ResponseContent::PutFH(status) )
));
named!(nfs4_res_putrootfh<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> ( Nfs4ResponseContent::PutRootFH(status) )
));
named!(nfs4_res_delegreturn<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> ( Nfs4ResponseContent::DelegReturn(status) )
));
named!(nfs4_res_setclientid<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> client_id: be_u64
>> verifier: be_u32
>> ( Nfs4ResponseContent::SetClientId(status) )
));
named!(nfs4_res_setclientid_confirm<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> ( Nfs4ResponseContent::SetClientIdConfirm(status) )
));
#[derive(Debug,PartialEq)]
pub struct Nfs4ResponseAccess {
pub supported_types: u32,
pub access_rights: u32,
}
named!(nfs4_res_access_ok<Nfs4ResponseAccess>,
do_parse!(
s: be_u32
>> a: be_u32
>> (Nfs4ResponseAccess {
supported_types: s,
access_rights: a,
})
));
named!(nfs4_res_access<Nfs4ResponseContent>,
do_parse!(
status: be_u32
>> ad: cond!(status == 0, nfs4_res_access_ok)
>> ( Nfs4ResponseContent::Access(
status, ad, ))
));
named!(nfs4_res_compound_command<Nfs4ResponseContent>,
do_parse!(
cmd: be_u32
>> cmd_data: switch!(value!(cmd),
NFSPROC4_READ => call!(nfs4_res_read) |
NFSPROC4_WRITE => call!(nfs4_res_write) |
NFSPROC4_ACCESS => call!(nfs4_res_access) |
NFSPROC4_GETFH => call!(nfs4_res_getfh) |
NFSPROC4_PUTFH => call!(nfs4_res_putfh) |
NFSPROC4_SAVEFH => call!(nfs4_res_savefh) |
NFSPROC4_RENAME => call!(nfs4_res_rename) |
NFSPROC4_READDIR => call!(nfs4_res_readdir) |
NFSPROC4_GETATTR => call!(nfs4_res_getattr) |
NFSPROC4_SETATTR => call!(nfs4_res_setattr) |
NFSPROC4_LOOKUP => call!(nfs4_res_lookup) |
NFSPROC4_OPEN => call!(nfs4_res_open) |
NFSPROC4_OPEN_CONFIRM => call!(nfs4_res_openconfirm) |
NFSPROC4_CLOSE => call!(nfs4_res_close) |
NFSPROC4_REMOVE => call!(nfs4_res_remove) |
NFSPROC4_CREATE => call!(nfs4_res_create) |
NFSPROC4_DELEGRETURN => call!(nfs4_res_delegreturn) |
NFSPROC4_SETCLIENTID => call!(nfs4_res_setclientid) |
NFSPROC4_SETCLIENTID_CONFIRM => call!(nfs4_res_setclientid_confirm) |
NFSPROC4_PUTROOTFH => call!(nfs4_res_putrootfh) |
NFSPROC4_RENEW => call!(nfs4_res_renew))
>> (cmd_data)
));
#[derive(Debug,PartialEq)]
pub struct Nfs4ResponseCompoundRecord<'a> {
pub status: u32,
pub commands: Vec<Nfs4ResponseContent<'a>>,
}
named!(pub parse_nfs4_response_compound<Nfs4ResponseCompoundRecord>,
do_parse!(
status: be_u32
>> tag_len: be_u32
>> tag: cond!(tag_len > 0, take!(tag_len))
>> ops_cnt: be_u32
>> commands: count!(nfs4_res_compound_command, ops_cnt as usize)
>> (Nfs4ResponseCompoundRecord {
status: status,
commands: commands,
})
));

@ -224,3 +224,99 @@ pub fn rpc_auth_status_string(auth_status: u32) -> String {
},
}.to_string()
}
pub const NFSPROC4_NULL: u32 = 0;
pub const NFSPROC4_COMPOUND: u32 = 1;
/* ops */
pub const NFSPROC4_ACCESS: u32 = 3;
pub const NFSPROC4_CLOSE: u32 = 4;
pub const NFSPROC4_COMMIT: u32 = 5;
pub const NFSPROC4_CREATE: u32 = 6;
pub const NFSPROC4_DELEGPURGE: u32 = 7;
pub const NFSPROC4_DELEGRETURN: u32 = 8;
pub const NFSPROC4_GETATTR: u32 = 9;
pub const NFSPROC4_GETFH: u32 = 10;
pub const NFSPROC4_LINK: u32 = 11;
pub const NFSPROC4_LOCK: u32 = 12;
pub const NFSPROC4_LOCKT: u32 = 13;
pub const NFSPROC4_LOCKU: u32 = 14;
pub const NFSPROC4_LOOKUP: u32 = 15;
pub const NFSPROC4_LOOKUPP: u32 = 16;
pub const NFSPROC4_NVERIFY: u32 = 17;
pub const NFSPROC4_OPEN: u32 = 18;
pub const NFSPROC4_OPENATTR: u32 = 19;
pub const NFSPROC4_OPEN_CONFIRM: u32 = 20;
pub const NFSPROC4_OPEN_DOWNGRADE: u32 = 21;
pub const NFSPROC4_PUTFH: u32 = 22;
pub const NFSPROC4_PUTPUBFH: u32 = 23;
pub const NFSPROC4_PUTROOTFH: u32 = 24;
pub const NFSPROC4_READ: u32 = 25;
pub const NFSPROC4_READDIR: u32 = 26;
pub const NFSPROC4_READLINK: u32 = 27;
pub const NFSPROC4_REMOVE: u32 = 28;
pub const NFSPROC4_RENAME: u32 = 29;
pub const NFSPROC4_RENEW: u32 = 30;
pub const NFSPROC4_RESTOREFH: u32 = 31;
pub const NFSPROC4_SAVEFH: u32 = 32;
pub const NFSPROC4_SECINFO: u32 = 33;
pub const NFSPROC4_SETATTR: u32 = 34;
pub const NFSPROC4_SETCLIENTID: u32 = 35;
pub const NFSPROC4_SETCLIENTID_CONFIRM: u32 = 36;
pub const NFSPROC4_VERIFY: u32 = 37;
pub const NFSPROC4_WRITE: u32 = 38;
pub const NFSPROC4_RELEASE_LOCKOWNER: u32 = 39;
pub const NFSPROC4_EXCHANGE_ID: u32 = 42;
pub const NFSPROC4_ILLEGAL: u32 = 10044;
pub fn nfs4_procedure_string(procedure: u32) -> String {
match procedure {
NFSPROC4_COMPOUND => "COMPOUND",
NFSPROC4_NULL => "NULL",
// ops
NFSPROC4_ACCESS => "ACCESS",
NFSPROC4_CLOSE => "CLOSE",
NFSPROC4_COMMIT => "COMMIT",
NFSPROC4_CREATE => "CREATE",
NFSPROC4_DELEGPURGE => "DELEGPURGE",
NFSPROC4_DELEGRETURN => "DELEGRETURN",
NFSPROC4_GETATTR => "GETATTR",
NFSPROC4_GETFH => "GETFH",
NFSPROC4_LINK => "LINK",
NFSPROC4_LOCK => "LOCK",
NFSPROC4_LOCKT => "LOCKT",
NFSPROC4_LOCKU => "LOCKU",
NFSPROC4_LOOKUP => "LOOKUP",
NFSPROC4_LOOKUPP => "LOOKUPP",
NFSPROC4_NVERIFY => "NVERIFY",
NFSPROC4_OPEN => "OPEN",
NFSPROC4_OPENATTR => "OPENATTR",
NFSPROC4_OPEN_CONFIRM => "OPEN_CONFIRM",
NFSPROC4_OPEN_DOWNGRADE => "OPEN_DOWNGRADE",
NFSPROC4_PUTFH => "PUTFH",
NFSPROC4_PUTPUBFH => "PUTPUBFH",
NFSPROC4_PUTROOTFH => "PUTROOTFH",
NFSPROC4_READ => "READ",
NFSPROC4_READDIR => "READDIR",
NFSPROC4_READLINK => "READLINK",
NFSPROC4_REMOVE => "REMOVE",
NFSPROC4_RENAME => "RENAME",
NFSPROC4_RENEW => "RENEW",
NFSPROC4_RESTOREFH => "RESTOREFH",
NFSPROC4_SAVEFH => "SAVEFH",
NFSPROC4_SECINFO => "SECINFO",
NFSPROC4_SETATTR => "SETATTR",
NFSPROC4_SETCLIENTID => "SETCLIENTID",
NFSPROC4_SETCLIENTID_CONFIRM => "SETCLIENTID_CONFIRM",
NFSPROC4_VERIFY => "VERIFY",
NFSPROC4_WRITE => "WRITE",
NFSPROC4_RELEASE_LOCKOWNER => "RELEASE_LOCKOWNER",
NFSPROC4_ILLEGAL => "ILLEGAL",
_ => {
return (procedure).to_string();
}
}.to_string()
}

Loading…
Cancel
Save