mirror of https://github.com/OISF/suricata
nfs4: initial implementation
Implements record parsing and file extraction for READs and WRITEs. Defines all types from RFC 7530.pull/3335/head
parent
75c5722b7e
commit
06f6c15954
@ -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,
|
||||
})
|
||||
));
|
Loading…
Reference in New Issue