/* 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 nom7::bytes::streaming::{tag, take}; use nom7::combinator::{complete, cond, map, peek, verify, rest}; use nom7::error::{make_error, ErrorKind}; use nom7::multi::{count, many_till}; use nom7::number::streaming::{be_u32, be_u64}; use nom7::{Err, IResult}; use crate::nfs::types::*; /*https://datatracker.ietf.org/doc/html/rfc7530 - section 16.16 File Delegation Types */ const OPEN_DELEGATE_NONE: u32 = 0; const OPEN_DELEGATE_READ: u32 = 1; const OPEN_DELEGATE_WRITE: u32 = 2; const RPCSEC_GSS: u32 = 6; // Maximum number of operations per compound // Linux defines NFSD_MAX_OPS_PER_COMPOUND to 16 (tested in Linux 5.15.1). const NFSD_MAX_OPS_PER_COMPOUND: usize = 64; #[derive(Debug,PartialEq, Eq)] pub enum Nfs4RequestContent<'a> { PutFH(Nfs4Handle<'a>), GetFH, SaveFH, PutRootFH, ReadDir, Commit, 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>), Sequence(Nfs4RequestSequence<'a>), CreateSession(Nfs4RequestCreateSession<'a>), ReclaimComplete(u32), SecInfoNoName(u32), LayoutGet(Nfs4RequestLayoutGet<'a>), GetDevInfo(Nfs4RequestGetDevInfo<'a>), LayoutReturn(Nfs4RequestLayoutReturn<'a>), DestroySession(&'a[u8]), DestroyClientID(&'a[u8]), } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4Attr { attr_mask: u64, } fn nfs4_parse_attr_fields(i: &[u8]) -> IResult<&[u8], u32> { let (i, len) = be_u32(i)?; let (i, _) = take(len as usize)(i)?; Ok((i, len)) } fn nfs4_parse_attrs(i: &[u8]) -> IResult<&[u8], Nfs4Attr> { let (i, attr_cnt) = be_u32(i)?; let (i, attr_mask1) = be_u32(i)?; let (i, attr_mask2) = cond(attr_cnt >= 2, be_u32)(i)?; let (i, _) = cond(attr_cnt == 3, be_u32)(i)?; let (i, _) = nfs4_parse_attr_fields(i)?; let attr = Nfs4Attr { attr_mask: ((attr_mask1 as u64) << 32) | attr_mask2.unwrap_or(0) as u64, }; Ok((i, attr)) } fn nfs4_parse_attrbits(i: &[u8]) -> IResult<&[u8], Nfs4Attr> { let (i, attr_cnt) = be_u32(i)?; let (i, attr_mask1) = be_u32(i)?; let (i, attr_mask2) = cond(attr_cnt >= 2, be_u32)(i)?; let (i, _) = cond(attr_cnt == 3, be_u32)(i)?; let attr = Nfs4Attr { attr_mask: ((attr_mask1 as u64) << 32) | attr_mask2.unwrap_or(0) as u64, }; Ok((i, attr)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4StateId<'a> { pub seqid: u32, pub data: &'a[u8], } fn nfs4_parse_stateid(i: &[u8]) -> IResult<&[u8], Nfs4StateId> { let (i, seqid) = be_u32(i)?; let (i, data) = take(12_usize)(i)?; let state = Nfs4StateId { seqid, data }; Ok((i, state)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4Handle<'a> { pub len: u32, pub value: &'a[u8], } fn nfs4_parse_handle(i: &[u8]) -> IResult<&[u8], Nfs4Handle> { let (i, len) = be_u32(i)?; let (i, value) = take(len as usize)(i)?; let handle = Nfs4Handle { len, value }; Ok((i, handle)) } fn nfs4_parse_nfsstring(i: &[u8]) -> IResult<&[u8], &[u8]> { let (i, len) = be_u32(i)?; let (i, data) = take(len as usize)(i)?; let (i, _fill_bytes) = cond(len % 4 != 0, take(4 - (len % 4)))(i)?; Ok((i, data)) } #[derive(Debug, PartialEq, Eq)] pub struct Nfs4RequestLayoutReturn<'a> { pub layout_type: u32, pub return_type: u32, pub length: u64, pub stateid: Nfs4StateId<'a>, pub lrf_data: &'a[u8], } fn nfs4_req_layoutreturn(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, _reclaim) = verify(be_u32, |&v| v <= 1)(i)?; let (i, layout_type) = be_u32(i)?; let (i, _iq_mode) = be_u32(i)?; let (i, return_type) = be_u32(i)?; let (i, _offset) = be_u64(i)?; let (i, length) = be_u64(i)?; let (i, stateid) = nfs4_parse_stateid(i)?; let (i, lrf_data) = nfs4_parse_nfsstring(i)?; let req = Nfs4RequestContent::LayoutReturn(Nfs4RequestLayoutReturn { layout_type, return_type, length, stateid, lrf_data, }); Ok((i, req)) } #[derive(Debug, PartialEq, Eq)] pub struct Nfs4RequestGetDevInfo<'a> { pub device_id: &'a[u8], pub layout_type: u32, pub maxcount: u32, pub notify_mask: u32, } fn nfs4_req_getdevinfo(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, device_id) = take(16_usize)(i)?; let (i, layout_type) = be_u32(i)?; let (i, maxcount) = be_u32(i)?; let (i, _) = be_u32(i)?; let (i, notify_mask) = be_u32(i)?; let req = Nfs4RequestContent::GetDevInfo(Nfs4RequestGetDevInfo { device_id, layout_type, maxcount, notify_mask, }); Ok((i, req)) } #[derive(Debug, PartialEq, Eq)] pub struct Nfs4RequestCreateSession<'a> { pub client_id: &'a[u8], pub seqid: u32, pub machine_name: &'a[u8], } fn nfs4_req_create_session(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, client_id) = take(8_usize)(i)?; let (i, seqid) = be_u32(i)?; let (i, _flags) = be_u32(i)?; let (i, _fore_chan_attrs) = take(28_usize)(i)?; let (i, _back_chan_attrs) = take(28_usize)(i)?; let (i, _cb_program) = be_u32(i)?; let (i, _) = be_u32(i)?; let (i, _flavor) = be_u32(i)?; let (i, _stamp) = be_u32(i)?; let (i, machine_name) = nfs4_parse_nfsstring(i)?; let (i, _) = rest(i)?; let req = Nfs4RequestContent::CreateSession(Nfs4RequestCreateSession { client_id, seqid, machine_name, }); Ok((i, req)) } fn nfs4_req_putfh(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { map(nfs4_parse_handle, Nfs4RequestContent::PutFH)(i) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestSetClientId<'a> { pub client_id: &'a[u8], pub r_netid: &'a[u8], pub r_addr: &'a[u8], } fn nfs4_req_setclientid(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, _client_verifier) = take(8_usize)(i)?; let (i, client_id) = nfs4_parse_nfsstring(i)?; let (i, _cb_program) = be_u32(i)?; let (i, r_netid) = nfs4_parse_nfsstring(i)?; let (i, r_addr) = nfs4_parse_nfsstring(i)?; let (i, _cb_id) = be_u32(i)?; let req = Nfs4RequestContent::SetClientId(Nfs4RequestSetClientId { client_id, r_netid, r_addr }); Ok((i, req)) } fn nfs4_req_setclientid_confirm(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, _client_id) = take(8_usize)(i)?; let (i, _verifier) = take(8_usize)(i)?; Ok((i, Nfs4RequestContent::SetClientIdConfirm)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestCreate<'a> { pub ftype4: u32, pub filename: &'a[u8], pub link_content: &'a[u8], } fn nfs4_req_create(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, ftype4) = be_u32(i)?; let (i, link_content) = cond(ftype4 == 5, nfs4_parse_nfsstring)(i)?; let (i, filename) = nfs4_parse_nfsstring(i)?; let (i, _attrs) = nfs4_parse_attrs(i)?; let req = Nfs4RequestContent::Create(Nfs4RequestCreate { ftype4, filename, link_content: link_content.unwrap_or(&[]), }); Ok((i, req)) } #[derive(Debug,PartialEq, Eq)] pub enum Nfs4OpenRequestContent<'a> { Exclusive4(&'a[u8]), Unchecked4(Nfs4Attr), Guarded4(Nfs4Attr), } fn nfs4_req_open_unchecked4(i: &[u8]) -> IResult<&[u8], Nfs4OpenRequestContent> { map(nfs4_parse_attrs, Nfs4OpenRequestContent::Unchecked4)(i) } fn nfs4_req_open_guarded4(i: &[u8]) -> IResult<&[u8], Nfs4OpenRequestContent> { map(nfs4_parse_attrs, Nfs4OpenRequestContent::Guarded4)(i) } fn nfs4_req_open_exclusive4(i: &[u8]) -> IResult<&[u8], Nfs4OpenRequestContent> { map(take(8_usize), Nfs4OpenRequestContent::Exclusive4)(i) } fn nfs4_req_open_type(i: &[u8]) -> IResult<&[u8], Nfs4OpenRequestContent> { let (i, mode) = be_u32(i)?; let (i, data) = match mode { 0 => nfs4_req_open_unchecked4(i)?, 1 => nfs4_req_open_guarded4(i)?, 2 => nfs4_req_open_exclusive4(i)?, _ => { return Err(Err::Error(make_error(i, ErrorKind::Switch))); } }; Ok((i, data)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestOpen<'a> { pub open_type: u32, pub filename: &'a[u8], pub open_data: Option>, } fn nfs4_req_open(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, _seq_id) = be_u32(i)?; let (i, _share_access) = be_u32(i)?; let (i, _share_deny) = be_u32(i)?; let (i, _client_id) = be_u64(i)?; let (i, owner_len) = be_u32(i)?; let (i, _) = cond(owner_len > 0, take(owner_len as usize))(i)?; let (i, open_type) = be_u32(i)?; let (i, open_data) = cond(open_type == 1, nfs4_req_open_type)(i)?; let (i, _claim_type) = be_u32(i)?; let (i, filename) = nfs4_parse_nfsstring(i)?; let req = Nfs4RequestContent::Open(Nfs4RequestOpen { open_type, filename, open_data }); Ok((i, req)) } fn nfs4_req_readdir(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, _cookie) = be_u64(i)?; let (i, _cookie_verf) = be_u64(i)?; let (i, _dir_cnt) = be_u32(i)?; let (i, _max_cnt) = be_u32(i)?; let (i, _attr) = nfs4_parse_attrbits(i)?; Ok((i, Nfs4RequestContent::ReadDir)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestRename<'a> { pub oldname: &'a[u8], pub newname: &'a[u8], } fn nfs4_req_rename(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, oldname) = nfs4_parse_nfsstring(i)?; let (i, newname) = nfs4_parse_nfsstring(i)?; let req = Nfs4RequestContent::Rename(Nfs4RequestRename { oldname, newname }); Ok((i, req)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestLookup<'a> { pub filename: &'a[u8], } fn nfs4_req_destroy_session(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, ssn_id) = take(16_usize)(i)?; Ok((i, Nfs4RequestContent::DestroySession(ssn_id))) } fn nfs4_req_lookup(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { map(nfs4_parse_nfsstring, |filename| { Nfs4RequestContent::Lookup(Nfs4RequestLookup { filename }) })(i) } fn nfs4_req_remove(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { map(nfs4_parse_nfsstring, Nfs4RequestContent::Remove)(i) } fn nfs4_req_secinfo_no_name(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { map(be_u32, Nfs4RequestContent::SecInfoNoName) (i) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestSetAttr<'a> { pub stateid: Nfs4StateId<'a>, } fn nfs4_req_setattr(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, stateid) = nfs4_parse_stateid(i)?; let (i, _attrs) = nfs4_parse_attrs(i)?; let req = Nfs4RequestContent::SetAttr(Nfs4RequestSetAttr { stateid }); Ok((i, req)) } fn nfs4_req_getattr(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { map(nfs4_parse_attrbits, Nfs4RequestContent::GetAttr)(i) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestWrite<'a> { pub stateid: Nfs4StateId<'a>, pub offset: u64, pub stable: u32, pub write_len: u32, pub data: &'a[u8], } fn nfs4_req_write(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, stateid) = nfs4_parse_stateid(i)?; let (i, offset) = be_u64(i)?; let (i, stable) = be_u32(i)?; let (i, write_len) = be_u32(i)?; let (i, data) = take(write_len as usize)(i)?; let (i, _padding) = cond(write_len % 4 != 0, take(4 - (write_len % 4)))(i)?; let req = Nfs4RequestContent::Write(Nfs4RequestWrite { stateid, offset, stable, write_len, data, }); Ok((i, req)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestRead<'a> { pub stateid: Nfs4StateId<'a>, pub offset: u64, pub count: u32, } fn nfs4_req_read(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, stateid) = nfs4_parse_stateid(i)?; let (i, offset) = be_u64(i)?; let (i, count) = be_u32(i)?; let req = Nfs4RequestContent::Read(Nfs4RequestRead { stateid, offset, count, }); Ok((i, req)) } fn nfs4_req_close(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, _seq_id) = be_u32(i)?; let (i, stateid) = nfs4_parse_stateid(i)?; Ok((i, Nfs4RequestContent::Close(stateid))) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestOpenConfirm<'a> { pub stateid: Nfs4StateId<'a>, } fn nfs4_req_open_confirm(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, _seq_id) = be_u32(i)?; let (i, stateid) = nfs4_parse_stateid(i)?; let req = Nfs4RequestContent::OpenConfirm(Nfs4RequestOpenConfirm { stateid }); Ok((i, req)) } fn nfs4_req_delegreturn(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { map(nfs4_parse_stateid, Nfs4RequestContent::DelegReturn)(i) } fn nfs4_req_renew(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { map(be_u64, Nfs4RequestContent::Renew)(i) } fn nfs4_req_getfh(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { Ok((i, Nfs4RequestContent::GetFH)) } fn nfs4_req_savefh(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { Ok((i, Nfs4RequestContent::SaveFH)) } fn nfs4_req_putrootfh(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { Ok((i, Nfs4RequestContent::PutRootFH)) } fn nfs4_req_access(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { map(be_u32, Nfs4RequestContent::Access)(i) } fn nfs4_req_commit(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, _offset) = be_u64(i)?; let (i, _count) = be_u32(i)?; Ok((i, Nfs4RequestContent::Commit)) } fn nfs4_req_reclaim_complete(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { map(verify(be_u32, |&v| v <= 1), Nfs4RequestContent::ReclaimComplete) (i) } fn nfs4_req_destroy_clientid(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, client_id) = take(8_usize)(i)?; Ok((i, Nfs4RequestContent::DestroyClientID(client_id))) } #[derive(Debug, PartialEq, Eq)] pub struct Nfs4RequestLayoutGet<'a> { pub layout_type: u32, pub length: u64, pub min_length: u64, pub stateid: Nfs4StateId<'a>, } fn nfs4_req_layoutget(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, _layout_available) = verify(be_u32, |&v| v <= 1)(i)?; let (i, layout_type) = be_u32(i)?; let (i, _iq_mode) = be_u32(i)?; let (i, _offset) = be_u64(i)?; let (i, length) = be_u64(i)?; let (i, min_length) = be_u64(i)?; let (i, stateid) = nfs4_parse_stateid(i)?; let (i, _maxcount) = be_u32(i)?; let req = Nfs4RequestContent::LayoutGet(Nfs4RequestLayoutGet { layout_type, length, min_length, stateid, }); Ok((i, req)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestExchangeId<'a> { pub client_string: &'a[u8], pub nii_domain: &'a[u8], pub nii_name: &'a[u8], } fn nfs4_req_exchangeid(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, _verifier) = take(8_usize)(i)?; let (i, eia_clientstring) = nfs4_parse_nfsstring(i)?; let (i, _eia_clientflags) = be_u32(i)?; let (i, _eia_state_protect) = be_u32(i)?; let (i, _eia_client_impl_id) = be_u32(i)?; let (i, nii_domain) = nfs4_parse_nfsstring(i)?; let (i, nii_name) = nfs4_parse_nfsstring(i)?; let (i, _nii_data_sec) = be_u64(i)?; let (i, _nii_data_nsec) = be_u32(i)?; let req = Nfs4RequestContent::ExchangeId(Nfs4RequestExchangeId { client_string: eia_clientstring, nii_domain, nii_name }); Ok((i, req)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestSequence<'a> { pub ssn_id: &'a[u8], } fn nfs4_req_sequence(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, ssn_id) = take(16_usize)(i)?; let (i, _seq_id) = be_u32(i)?; let (i, _slot_id) = be_u32(i)?; let (i, _high_slot_id) = be_u32(i)?; let (i, _cache_this) = be_u32(i)?; let req = Nfs4RequestContent::Sequence(Nfs4RequestSequence { ssn_id }); Ok((i, req)) } fn parse_request_compound_command(i: &[u8]) -> IResult<&[u8], Nfs4RequestContent> { let (i, cmd) = be_u32(i)?; let (i, cmd_data) = match cmd { NFSPROC4_PUTFH => nfs4_req_putfh(i)?, NFSPROC4_READ => nfs4_req_read(i)?, NFSPROC4_WRITE => nfs4_req_write(i)?, NFSPROC4_GETFH => nfs4_req_getfh(i)?, NFSPROC4_SAVEFH => nfs4_req_savefh(i)?, NFSPROC4_OPEN => nfs4_req_open(i)?, NFSPROC4_CLOSE => nfs4_req_close(i)?, NFSPROC4_LOOKUP => nfs4_req_lookup(i)?, NFSPROC4_ACCESS => nfs4_req_access(i)?, NFSPROC4_COMMIT => nfs4_req_commit(i)?, NFSPROC4_GETATTR => nfs4_req_getattr(i)?, NFSPROC4_READDIR => nfs4_req_readdir(i)?, NFSPROC4_RENEW => nfs4_req_renew(i)?, NFSPROC4_OPEN_CONFIRM => nfs4_req_open_confirm(i)?, NFSPROC4_REMOVE => nfs4_req_remove(i)?, NFSPROC4_RENAME => nfs4_req_rename(i)?, NFSPROC4_CREATE => nfs4_req_create(i)?, NFSPROC4_DELEGRETURN => nfs4_req_delegreturn(i)?, NFSPROC4_SETATTR => nfs4_req_setattr(i)?, NFSPROC4_PUTROOTFH => nfs4_req_putrootfh(i)?, NFSPROC4_SETCLIENTID => nfs4_req_setclientid(i)?, NFSPROC4_SETCLIENTID_CONFIRM => nfs4_req_setclientid_confirm(i)?, NFSPROC4_SEQUENCE => nfs4_req_sequence(i)?, NFSPROC4_EXCHANGE_ID => nfs4_req_exchangeid(i)?, NFSPROC4_CREATE_SESSION => nfs4_req_create_session(i)?, NFSPROC4_RECLAIM_COMPLETE => nfs4_req_reclaim_complete(i)?, NFSPROC4_SECINFO_NO_NAME => nfs4_req_secinfo_no_name(i)?, NFSPROC4_LAYOUTGET => nfs4_req_layoutget(i)?, NFSPROC4_GETDEVINFO => nfs4_req_getdevinfo(i)?, NFSPROC4_LAYOUTRETURN => nfs4_req_layoutreturn(i)?, NFSPROC4_DESTROY_SESSION => nfs4_req_destroy_session(i)?, NFSPROC4_DESTROY_CLIENTID => nfs4_req_destroy_clientid(i)?, _ => { return Err(Err::Error(make_error(i, ErrorKind::Switch))); } }; Ok((i, cmd_data)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4RequestCompoundRecord<'a> { pub commands: Vec>, } pub fn parse_nfs4_request_compound(i: &[u8]) -> IResult<&[u8], Nfs4RequestCompoundRecord> { let (i, tag_len) = be_u32(i)?; let (i, _tag) = cond(tag_len > 0, take(tag_len as usize))(i)?; let (i, _min_ver) = be_u32(i)?; let (i, ops_cnt) = be_u32(i)?; if ops_cnt as usize > NFSD_MAX_OPS_PER_COMPOUND { return Err(Err::Error(make_error(i, ErrorKind::Count))); } let (i, commands) = count(parse_request_compound_command, ops_cnt as usize)(i)?; Ok((i, Nfs4RequestCompoundRecord { commands })) } #[derive(Debug,PartialEq, Eq)] pub enum Nfs4ResponseContent<'a> { PutFH(u32), PutRootFH(u32), GetFH(u32, Option>), Lookup(u32), SaveFH(u32), Rename(u32), Write(u32, Option), Read(u32, Option>), Renew(u32), Open(u32, Option>), OpenConfirm(u32, Option>), Close(u32, Option>), GetAttr(u32, Option), SetAttr(u32), Access(u32, Option), ReadDir(u32, Option>), Remove(u32), DelegReturn(u32), SetClientId(u32), SetClientIdConfirm(u32), Create(u32), Commit(u32), ExchangeId(u32, Option>), Sequence(u32, Option>), CreateSession(u32, Option>), ReclaimComplete(u32), SecInfoNoName(u32), LayoutGet(u32, Option>), GetDevInfo(u32, Option>), LayoutReturn(u32), DestroySession(u32), DestroyClientID(u32), } // might need improvement with a stateid_present = yes case fn nfs4_res_layoutreturn(i:&[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, _stateid_present) = verify(be_u32, |&v| v <= 1)(i)?; Ok((i, Nfs4ResponseContent::LayoutReturn(status))) } #[derive(Debug, PartialEq, Eq)] pub struct Nfs4ResponseCreateSession<'a> { pub ssn_id: &'a[u8], pub seq_id: u32, } fn nfs4_parse_res_create_session(i: &[u8]) -> IResult<&[u8], Nfs4ResponseCreateSession> { let (i, ssn_id) = take(16_usize)(i)?; let (i, seq_id) = be_u32(i)?; let (i, _flags) = be_u32(i)?; let (i, _fore_chan_attrs) = take(28_usize)(i)?; let (i, _back_chan_attrs) = take(28_usize)(i)?; Ok((i, Nfs4ResponseCreateSession { ssn_id, seq_id })) } fn nfs4_res_create_session(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, create_ssn_data) = cond(status == 0, nfs4_parse_res_create_session)(i)?; Ok((i, Nfs4ResponseContent::CreateSession( status, create_ssn_data ))) } #[derive(Debug, PartialEq, Eq)] pub struct Nfs4ResponseExchangeId<'a> { pub client_id: &'a[u8], pub eir_minorid: u64, pub eir_majorid: &'a[u8], pub nii_domain: &'a[u8], pub nii_name: &'a[u8], } fn nfs4_parse_res_exchangeid(i: &[u8]) -> IResult<&[u8], Nfs4ResponseExchangeId> { let (i, client_id) = take(8_usize)(i)?; let (i, _seqid) = be_u32(i)?; let (i, _flags) = be_u32(i)?; let (i, _eia_state_protect) = be_u32(i)?; let (i, eir_minorid) = be_u64(i)?; let (i, eir_majorid) = nfs4_parse_nfsstring(i)?; let (i, _server_scope) = nfs4_parse_nfsstring(i)?; let (i, _eir_impl_id) = be_u32(i)?; let (i, nii_domain) = nfs4_parse_nfsstring(i)?; let (i, nii_name) = nfs4_parse_nfsstring(i)?; let (i, _nii_date_sec) = be_u64(i)?; let (i, _nii_date_nsec) = be_u32(i)?; Ok((i, Nfs4ResponseExchangeId { client_id, eir_minorid, eir_majorid, nii_domain, nii_name, })) } fn nfs4_res_reclaim_complete(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::ReclaimComplete) (i) } fn nfs4_res_exchangeid(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, xchngid_data) = cond(status == 0, nfs4_parse_res_exchangeid)(i)?; Ok((i, Nfs4ResponseContent::ExchangeId( status, xchngid_data))) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4ResponseWrite { pub count: u32, pub committed: u32, } fn nfs4_res_write_ok(i: &[u8]) -> IResult<&[u8], Nfs4ResponseWrite> { let (i, count) = be_u32(i)?; let (i, committed) = be_u32(i)?; let (i, _verifier) = be_u64(i)?; Ok((i, Nfs4ResponseWrite { count, committed })) } fn nfs4_res_write(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, wd) = cond(status == 0, nfs4_res_write_ok)(i)?; Ok((i, Nfs4ResponseContent::Write(status, wd))) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4ResponseRead<'a> { pub eof: bool, pub count: u32, pub data: &'a[u8], } fn nfs4_res_read_ok(i: &[u8]) -> IResult<&[u8], Nfs4ResponseRead> { let (i, eof) = verify(be_u32, |&v| v <= 1)(i)?; let (i, read_len) = be_u32(i)?; let (i, read_data) = take(read_len as usize)(i)?; let resp = Nfs4ResponseRead { eof: eof==1, count: read_len, data: read_data, }; Ok((i, resp)) } fn nfs4_res_read(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, rd) = cond(status == 0, nfs4_res_read_ok)(i)?; Ok((i, Nfs4ResponseContent::Read(status, rd))) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4ResponseOpen<'a> { pub stateid: Nfs4StateId<'a>, pub result_flags: u32, pub delegate: Nfs4ResponseFileDelegation<'a>, } #[derive(Debug, PartialEq, Eq)] pub enum Nfs4ResponseFileDelegation<'a> { DelegateRead(Nfs4ResponseOpenDelegateRead<'a>), DelegateWrite(Nfs4ResponseOpenDelegateWrite<'a>), DelegateNone(u32), } #[derive(Debug, PartialEq, Eq)] pub struct Nfs4ResponseOpenDelegateWrite<'a> { pub stateid: Nfs4StateId<'a>, pub who: &'a[u8], } fn nfs4_res_open_ok_delegate_write(i: &[u8]) -> IResult<&[u8], Nfs4ResponseFileDelegation> { let (i, stateid) = nfs4_parse_stateid(i)?; let (i, _recall) = be_u32(i)?; let (i, _space_limit) = be_u32(i)?; let (i, _filesize) = be_u32(i)?; let (i, _access_type) = be_u32(i)?; let (i, _ace_flags) = be_u32(i)?; let (i, _ace_mask) = be_u32(i)?; let (i, who) = nfs4_parse_nfsstring(i)?; Ok((i, Nfs4ResponseFileDelegation::DelegateWrite(Nfs4ResponseOpenDelegateWrite { stateid, who, }))) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4ResponseOpenDelegateRead<'a> { pub stateid: Nfs4StateId<'a>, } fn nfs4_res_open_ok_delegate_read(i: &[u8]) -> IResult<&[u8], Nfs4ResponseFileDelegation> { let (i, stateid) = nfs4_parse_stateid(i)?; let (i, _recall) = be_u32(i)?; let (i, _ace_type) = be_u32(i)?; let (i, _ace_flags) = be_u32(i)?; let (i, _ace_mask) = be_u32(i)?; let (i, who_len) = be_u32(i)?; let (i, _who) = take(who_len as usize)(i)?; Ok((i, Nfs4ResponseFileDelegation::DelegateRead(Nfs4ResponseOpenDelegateRead { stateid, }))) } fn nfs4_parse_file_delegation(i: &[u8]) -> IResult<&[u8], Nfs4ResponseFileDelegation> { let (i, delegation_type) = be_u32(i)?; let (i, file_delegation) = match delegation_type { OPEN_DELEGATE_READ => nfs4_res_open_ok_delegate_read(i)?, OPEN_DELEGATE_WRITE => nfs4_res_open_ok_delegate_write(i)?, OPEN_DELEGATE_NONE => (i, Nfs4ResponseFileDelegation::DelegateNone(OPEN_DELEGATE_NONE)), _ => { return Err(Err::Error(make_error(i, ErrorKind::Switch))); } }; Ok((i, file_delegation)) } fn nfs4_res_open_ok(i: &[u8]) -> IResult<&[u8], Nfs4ResponseOpen> { let (i, stateid) = nfs4_parse_stateid(i)?; let (i, _change_info) = take(20_usize)(i)?; let (i, result_flags) = be_u32(i)?; let (i, _attrs) = nfs4_parse_attrbits(i)?; let (i, delegate) = nfs4_parse_file_delegation(i)?; let resp = Nfs4ResponseOpen { stateid, result_flags, delegate, }; Ok((i, resp)) } fn nfs4_res_open(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, open_data) = cond(status == 0, nfs4_res_open_ok)(i)?; Ok((i, Nfs4ResponseContent::Open(status, open_data))) } #[derive(Debug, PartialEq, Eq)] pub struct Nfs4ResponseGetDevInfo<'a> { pub layout_type: u32, pub r_netid: &'a[u8], pub r_addr: &'a[u8], pub notify_mask: u32, } fn nfs4_parse_res_getdevinfo(i: &[u8]) -> IResult<&[u8], Nfs4ResponseGetDevInfo> { let (i, layout_type) = be_u32(i)?; let (i, _) = be_u64(i)?; let (i, _device_index) = be_u32(i)?; let (i, _) = be_u64(i)?; let (i, r_netid) = nfs4_parse_nfsstring(i)?; let (i, r_addr) = nfs4_parse_nfsstring(i)?; let (i, notify_mask) = be_u32(i)?; Ok((i, Nfs4ResponseGetDevInfo { layout_type, r_netid, r_addr, notify_mask, })) } fn nfs4_res_getdevinfo(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, getdevinfo) = cond(status == 0, nfs4_parse_res_getdevinfo)(i)?; Ok((i, Nfs4ResponseContent::GetDevInfo( status, getdevinfo ))) } /*https://datatracker.ietf.org/doc/html/rfc5661#section-13.1*/ // in case of multiple file handles, return handles in a vector #[derive(Debug, PartialEq, Eq)] pub struct Nfs4ResponseLayoutGet<'a> { pub stateid: Nfs4StateId<'a>, pub length: u64, pub layout_type: u32, pub device_id: &'a[u8], pub file_handles: Vec>, } fn nfs4_parse_res_layoutget(i: &[u8]) -> IResult<&[u8], Nfs4ResponseLayoutGet> { let (i, _return_on_close) = verify(be_u32, |&v| v <= 1)(i)?; let (i, stateid) = nfs4_parse_stateid(i)?; let (i, _layout_seg) = be_u32(i)?; let (i, _offset) = be_u64(i)?; let (i, length) = be_u64(i)?; let (i, _lo_mode) = be_u32(i)?; let (i, layout_type) = be_u32(i)?; let (i, _) = be_u32(i)?; let (i, device_id) = take(16_usize)(i)?; let (i, _nfl_util) = be_u32(i)?; let (i, _strip_index) = be_u32(i)?; let (i, _offset) = be_u64(i)?; let (i, fh_handles) = be_u32(i)?; // check before `count` allocates a vector // so as not to run out of memory if fh_handles as usize > 4 * i.len() { return Err(Err::Error(make_error(i, ErrorKind::Count))); } let (i, file_handles) = count(nfs4_parse_handle, fh_handles as usize)(i)?; Ok((i, Nfs4ResponseLayoutGet { stateid, length, layout_type, device_id, file_handles, })) } fn nfs4_res_layoutget(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, lyg_data) = cond(status == 0, nfs4_parse_res_layoutget)(i)?; Ok((i, Nfs4ResponseContent::LayoutGet( status, lyg_data ))) } // #[derive(Debug, PartialEq)] // pub struct Nfs4FlavorRpcSecGss<'a> { // pub oid: &'a[u8], // pub qop: u32, // pub service: u32, // } fn nfs4_parse_rpcsec_gss(i: &[u8]) -> IResult<&[u8], u32> { let (i, _oid) = nfs4_parse_nfsstring(i)?; let (i, _qop) = be_u32(i)?; let (i, _service) = be_u32(i)?; Ok((i, RPCSEC_GSS)) } fn nfs4_parse_flavors(i: &[u8]) -> IResult<&[u8], u32> { let (i, flavor_type) = be_u32(i)?; let (i, _flavor) = cond(flavor_type == RPCSEC_GSS, nfs4_parse_rpcsec_gss)(i)?; Ok((i, flavor_type)) } fn nfs4_res_secinfo_no_name(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, flavors_cnt) = be_u32(i)?; // do not use nom's count as it allocates a Vector first // which results in oom if flavors_cnt is really big, bigger than i.len() let mut i2 = i; for _n in 0..flavors_cnt { let (i3, _flavor) = nfs4_parse_flavors(i2)?; i2 = i3; } Ok((i, Nfs4ResponseContent::SecInfoNoName(status))) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4ResponseReaddirEntry<'a> { pub name: &'a[u8], } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4ResponseReaddir<'a> { pub eof: bool, pub listing: Vec>>, } fn nfs4_res_readdir_entry_do(i: &[u8]) -> IResult<&[u8], Nfs4ResponseReaddirEntry> { let (i, _cookie) = be_u64(i)?; let (i, name) = nfs4_parse_nfsstring(i)?; let (i, _attrs) = nfs4_parse_attrs(i)?; Ok((i, Nfs4ResponseReaddirEntry { name })) } fn nfs4_res_readdir_entry(i: &[u8]) -> IResult<&[u8], Option> { let (i, value_follows) = verify(be_u32, |&v| v <= 1)(i)?; let (i, entry) = cond(value_follows == 1, nfs4_res_readdir_entry_do)(i)?; Ok((i, entry)) } fn nfs4_res_readdir_ok(i: &[u8]) -> IResult<&[u8], Nfs4ResponseReaddir> { let (i, _verifier) = be_u64(i)?; // run parser until we find a 'value follows == 0' let (i, listing) = many_till( complete(nfs4_res_readdir_entry), peek(tag(b"\x00\x00\x00\x00")), )(i)?; // value follows == 0 checked by line above let (i, _value_follows) = tag(b"\x00\x00\x00\x00")(i)?; let (i, eof) = verify(be_u32, |&v| v <= 1)(i)?; Ok(( i, Nfs4ResponseReaddir { eof: eof == 1, listing: listing.0, }, )) } fn nfs4_res_readdir(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, rd) = cond(status == 0, nfs4_res_readdir_ok)(i)?; Ok((i, Nfs4ResponseContent::ReadDir(status, rd))) } fn nfs4_res_create_ok(i: &[u8]) -> IResult<&[u8], Nfs4Attr> { let (i, _change_info) = take(20_usize)(i)?; nfs4_parse_attrbits(i) } fn nfs4_res_create(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, _attrs) = cond(status == 0, nfs4_res_create_ok)(i)?; Ok((i, Nfs4ResponseContent::Create(status))) } fn nfs4_res_setattr_ok(i: &[u8]) -> IResult<&[u8], Nfs4Attr> { nfs4_parse_attrbits(i) } fn nfs4_res_setattr(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, _attrs) = cond(status == 0, nfs4_res_setattr_ok)(i)?; Ok((i, Nfs4ResponseContent::SetAttr(status))) } fn nfs4_res_getattr_ok(i: &[u8]) -> IResult<&[u8], Nfs4Attr> { nfs4_parse_attrs(i) } fn nfs4_res_getattr(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, attrs) = cond(status == 0, nfs4_res_getattr_ok)(i)?; Ok((i, Nfs4ResponseContent::GetAttr(status, attrs))) } fn nfs4_res_openconfirm(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, stateid) = cond(status == 0, nfs4_parse_stateid)(i)?; Ok((i, Nfs4ResponseContent::OpenConfirm(status, stateid))) } fn nfs4_res_close(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, stateid) = cond(status == 0, nfs4_parse_stateid)(i)?; Ok((i, Nfs4ResponseContent::Close(status, stateid))) } fn nfs4_res_remove(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, _) = cond(status == 0, take(20_usize))(i)?; Ok((i, Nfs4ResponseContent::Remove(status))) } fn nfs4_res_rename(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::Rename)(i) } fn nfs4_res_savefh(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::SaveFH)(i) } fn nfs4_res_lookup(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::Lookup)(i) } fn nfs4_res_renew(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::Renew)(i) } fn nfs4_res_getfh(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, fh) = cond(status == 0, nfs4_parse_handle)(i)?; Ok((i, Nfs4ResponseContent::GetFH(status, fh))) } fn nfs4_res_putfh(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::PutFH)(i) } fn nfs4_res_putrootfh(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::PutRootFH)(i) } fn nfs4_res_delegreturn(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::DelegReturn)(i) } fn nfs4_res_setclientid(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, _client_id) = be_u64(i)?; let (i, _verifier) = be_u32(i)?; Ok((i, Nfs4ResponseContent::SetClientId(status))) } fn nfs4_res_setclientid_confirm(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::SetClientIdConfirm)(i) } fn nfs4_res_commit(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, _verifier) = cond(status == 0, take(8_usize))(i)?; Ok((i, Nfs4ResponseContent::Commit(status))) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4ResponseAccess { pub supported_types: u32, pub access_rights: u32, } fn nfs4_res_access_ok(i: &[u8]) -> IResult<&[u8], Nfs4ResponseAccess> { let (i, supported_types) = be_u32(i)?; let (i, access_rights) = be_u32(i)?; let resp = Nfs4ResponseAccess { supported_types, access_rights }; Ok((i, resp)) } fn nfs4_res_access(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, ad) = cond(status == 0, nfs4_res_access_ok)(i)?; Ok((i, Nfs4ResponseContent::Access(status, ad))) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4ResponseSequence<'a> { pub ssn_id: &'a[u8], } fn nfs4_res_sequence_ok(i: &[u8]) -> IResult<&[u8], Nfs4ResponseSequence> { let (i, ssn_id) = take(16_usize)(i)?; let (i, _seqid) = be_u32(i)?; let (i, _slots) = take(12_usize)(i)?; let (i, _flags) = be_u32(i)?; Ok((i, Nfs4ResponseSequence { ssn_id })) } fn nfs4_res_sequence(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, status) = be_u32(i)?; let (i, seq) = cond(status == 0, nfs4_res_sequence_ok)(i)?; Ok((i, Nfs4ResponseContent::Sequence(status, seq))) } fn nfs4_res_destroy_session(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::DestroySession) (i) } fn nfs4_res_destroy_clientid(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { map(be_u32, Nfs4ResponseContent::DestroyClientID) (i) } fn nfs4_res_compound_command(i: &[u8]) -> IResult<&[u8], Nfs4ResponseContent> { let (i, cmd) = be_u32(i)?; let (i, cmd_data) = match cmd { NFSPROC4_READ => nfs4_res_read(i)?, NFSPROC4_WRITE => nfs4_res_write(i)?, NFSPROC4_ACCESS => nfs4_res_access(i)?, NFSPROC4_COMMIT => nfs4_res_commit(i)?, NFSPROC4_GETFH => nfs4_res_getfh(i)?, NFSPROC4_PUTFH => nfs4_res_putfh(i)?, NFSPROC4_SAVEFH => nfs4_res_savefh(i)?, NFSPROC4_RENAME => nfs4_res_rename(i)?, NFSPROC4_READDIR => nfs4_res_readdir(i)?, NFSPROC4_GETATTR => nfs4_res_getattr(i)?, NFSPROC4_SETATTR => nfs4_res_setattr(i)?, NFSPROC4_LOOKUP => nfs4_res_lookup(i)?, NFSPROC4_OPEN => nfs4_res_open(i)?, NFSPROC4_OPEN_CONFIRM => nfs4_res_openconfirm(i)?, NFSPROC4_CLOSE => nfs4_res_close(i)?, NFSPROC4_REMOVE => nfs4_res_remove(i)?, NFSPROC4_CREATE => nfs4_res_create(i)?, NFSPROC4_DELEGRETURN => nfs4_res_delegreturn(i)?, NFSPROC4_SETCLIENTID => nfs4_res_setclientid(i)?, NFSPROC4_SETCLIENTID_CONFIRM => nfs4_res_setclientid_confirm(i)?, NFSPROC4_PUTROOTFH => nfs4_res_putrootfh(i)?, NFSPROC4_EXCHANGE_ID => nfs4_res_exchangeid(i)?, NFSPROC4_SEQUENCE => nfs4_res_sequence(i)?, NFSPROC4_RENEW => nfs4_res_renew(i)?, NFSPROC4_CREATE_SESSION => nfs4_res_create_session(i)?, NFSPROC4_RECLAIM_COMPLETE => nfs4_res_reclaim_complete(i)?, NFSPROC4_SECINFO_NO_NAME => nfs4_res_secinfo_no_name(i)?, NFSPROC4_LAYOUTGET => nfs4_res_layoutget(i)?, NFSPROC4_GETDEVINFO => nfs4_res_getdevinfo(i)?, NFSPROC4_LAYOUTRETURN => nfs4_res_layoutreturn(i)?, NFSPROC4_DESTROY_SESSION => nfs4_res_destroy_session(i)?, NFSPROC4_DESTROY_CLIENTID => nfs4_res_destroy_clientid(i)?, _ => { return Err(Err::Error(make_error(i, ErrorKind::Switch))); } }; Ok((i, cmd_data)) } #[derive(Debug,PartialEq, Eq)] pub struct Nfs4ResponseCompoundRecord<'a> { pub status: u32, pub commands: Vec>, } pub fn parse_nfs4_response_compound(i: &[u8]) -> IResult<&[u8], Nfs4ResponseCompoundRecord> { let (i, status) = be_u32(i)?; let (i, tag_len) = be_u32(i)?; let (i, _tag) = cond(tag_len > 0, take(tag_len as usize))(i)?; let (i, ops_cnt) = be_u32(i)?; if ops_cnt as usize > NFSD_MAX_OPS_PER_COMPOUND { return Err(Err::Error(make_error(i, ErrorKind::Count))); } let (i, commands) = count(nfs4_res_compound_command, ops_cnt as usize)(i)?; Ok((i, Nfs4ResponseCompoundRecord { status, commands })) } #[cfg(test)] mod tests { use crate::nfs::nfs4_records::*; #[test] fn test_nfs4_request_compound() { // Operations: SEQUENCE, PUTFH, CLOSE #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, /*Tag*/ 0x00, 0x00, 0x00, 0x01, /*min_ver*/ 0x00, 0x00, 0x00, 0x03, /*ops_cnt*/ // SEQUENCE 0x00, 0x00, 0x00, 0x35, /*op_code*/ 0x00, 0x00, 0x02, 0xd2, 0xe0, 0x14, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // PUTFH 0x00, 0x00, 0x00, 0x16, /*op_code*/ 0x00, 0x00, 0x00, 0x20, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x72, 0x00, 0x00, 0x23, 0xa6, 0xc0, 0x12, 0x00, 0xf2, 0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CLOSE 0x00, 0x00, 0x00, 0x04, /*op_code*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x82, 0x14, 0xe0, 0x5b, 0x00, 0x88, 0xd9, 0x04, 0x00, 0x00, 0x00, ]; let sequence_buf: &[u8] = &buf[16..48]; let putfh_buf: &[u8] = &buf[52..88]; let close_buf: &[u8] = &buf[92..]; let (_, req_sequence) = nfs4_req_sequence(sequence_buf).unwrap(); let (_, req_putfh) = nfs4_req_putfh(putfh_buf).unwrap(); let (_, req_close) = nfs4_req_close(close_buf).unwrap(); let (_, compound_ops) = parse_nfs4_request_compound(buf).unwrap(); assert_eq!(compound_ops.commands[0], req_sequence); assert_eq!(compound_ops.commands[1], req_putfh); assert_eq!(compound_ops.commands[2], req_close); } #[test] fn test_nfs4_request_setclientid() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x23, /*opcode*/ 0x59, 0x1b, 0x09, 0x04, 0x28, 0x9c, 0x5d, 0x10, /*_verifier*/ 0x00, 0x00, 0x00, 0x2d, 0x4c, 0x69, 0x6e, 0x75, /*client_id*/ 0x78, 0x20, 0x4e, 0x46, 0x53, 0x76, 0x34, 0x2e, 0x30, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x39, 0x33, 0x2e, 0x36, 0x37, 0x2e, 0x32, 0x32, 0x35, 0x2f, 0x31, 0x30, 0x2e, 0x31, 0x39, 0x33, 0x2e, 0x36, 0x37, 0x2e, 0x32, 0x31, 0x39, 0x20, 0x74, 0x63, 0x70, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, /*_cb_program*/ 0x00, 0x00, 0x00, 0x03, 0x74, 0x63, 0x70, 0x00, /*r_netid*/ 0x00, 0x00, 0x00, 0x14, 0x31, 0x30, 0x2e, 0x31, /*r_addr*/ 0x39, 0x33, 0x2e, 0x36, 0x37, 0x2e, 0x32, 0x32, 0x35, 0x2e, 0x31, 0x34, 0x30, 0x2e, 0x31, 0x38, 0x00, 0x00, 0x00, 0x01, /*_cb_id*/ ]; let (_, req_client_id) = nfs4_parse_nfsstring(&buf[12..64]).unwrap(); let (_, req_r_netid) = nfs4_parse_nfsstring(&buf[68 ..76]).unwrap(); let (_, req_r_adrr) = nfs4_parse_nfsstring(&buf[76..100]).unwrap(); let (_, resquest) = nfs4_req_setclientid(&buf[4..]).unwrap(); match resquest { Nfs4RequestContent::SetClientId( req_setclientid ) => { assert_eq!(req_setclientid.client_id, req_client_id); assert_eq!(req_setclientid.r_netid, req_r_netid); assert_eq!(req_setclientid.r_addr, req_r_adrr); } _ => { panic!("Failure"); } } } #[test] fn test_nfs4_request_open() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x12, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*_seq_id*/ 0x00, 0x00, 0x00, 0x02, /*_share_access*/ 0x00, 0x00, 0x00, 0x00, /*_share_deny*/ 0xe0, 0x14, 0x82, 0x00, 0x00, 0x00, 0x02, 0xd2, /*_client_id*/ // OWNER 0x00, 0x00, 0x00, 0x18, /*owner_len*/ 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x69, 0x64, 0x3a, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x48, 0x0c, 0xae, 0x9b, 0x05, 0x08, // OPEN 0x00, 0x00, 0x00, 0x01, /*open_type: OPEN4_CREATE*/ 0x00, 0x00, 0x00, 0x00, /*create_mode: UNCHECKED4*/ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, /*attr_mask*/ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb4, // CLAIM_TYPE 0x00, 0x00, 0x00, 0x00, /*_claim_type: CLAIM_NULL*/ 0x00, 0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x65, /*filename*/ ]; let (_, attr_buf) = nfs4_parse_attrbits(&buf[60..88]).unwrap(); let (_, filename_buf) = nfs4_parse_nfsstring(&buf[92..]).unwrap(); let (_, request) = nfs4_req_open(&buf[4..]).unwrap(); match request { Nfs4RequestContent::Open(req_open) => { assert_eq!(req_open.open_type, 1); assert_eq!(req_open.open_data, Some(Nfs4OpenRequestContent::Unchecked4(attr_buf))); assert_eq!(req_open.filename, filename_buf); } _ => { panic!("Failure, {:?}", request); } } } #[test] fn test_nfs4_request_write() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x26, /*op_code*/ 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x14, 0xe0, /*stateid*/ 0x5b, 0x00, 0x89, 0xd9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*offset*/ 0x00, 0x00, 0x00, 0x02, /*stable*/ 0x00, 0x00, 0x00, 0x05, /*write_len*/ 0x74, 0x65, 0x73, 0x74, 0x0a, /*data*/ 0x00, 0x00, 0x00, /*_padding*/ ]; let (_, stateid_buf) = nfs4_parse_stateid(&buf[4..20]).unwrap(); let (_, request) = nfs4_req_write(&buf[4..]).unwrap(); match request { Nfs4RequestContent::Write(req_write) => { assert_eq!(req_write.stateid, stateid_buf); assert_eq!(req_write.offset, 0); assert_eq!(req_write.stable, 2); assert_eq!(req_write.write_len, 5); assert_eq!(req_write.data, "test\n".as_bytes()); } _ => { panic!("Failure, {:?}", request); } } } #[test] fn test_nfs4_request_exchangeid() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x2a, /*opcode*/ // eia_clientowner 0x5c, 0x8a, 0x9b, 0xfe, 0x0c, 0x09, 0x5e, 0x92, /*_verifier*/ 0x00, 0x00, 0x00, 0x17, 0x4c, 0x69, 0x6e, 0x75, /*eia_clientstring*/ 0x78, 0x20, 0x4e, 0x46, 0x53, 0x76, 0x34, 0x2e, 0x31, 0x20, 0x6e, 0x65, 0x74, 0x61, 0x70, 0x70, 0x2d, 0x32, 0x36, 0x00, 0x00, 0x00, 0x01, 0x01, /*_eia_clientflags*/ 0x00, 0x00, 0x00, 0x00, /*_eia_state_protect*/ // _eia_client_impl_id 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x72, 0x6e, /*nii_domain*/ 0x65, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x4c, 0x69, 0x6e, 0x75, /*nii_name*/ 0x78, 0x20, 0x33, 0x2e, 0x31, 0x30, 0x2e, 0x30, 0x2d, 0x39, 0x35, 0x37, 0x2e, 0x65, 0x6c, 0x37, 0x2e, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x20, 0x23, 0x31, 0x20, 0x53, 0x4d, 0x50, 0x20, 0x54, 0x68, 0x75, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x34, 0x20, 0x32, 0x30, 0x3a, 0x34, 0x38, 0x3a, 0x35, 0x31, 0x20, 0x55, 0x54, 0x43, 0x20, 0x32, 0x30, 0x31, 0x38, 0x20, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*_nii_data_sec*/ 0x00, 0x00, 0x00, 0x00, /*_nii_data_nsec*/ ]; /*( .Linux NFSv4.1 netapp-26 )*/ let (_, client_string_buf) = nfs4_parse_nfsstring(&buf[12..40]).unwrap(); /*(kernel.org\0\0\0\n)*/ let (_, nii_domain_buf) = nfs4_parse_nfsstring(&buf[52..68]).unwrap(); /* ( ELinux 3.10.0-957.el7.x86_64 #1 SMP Thu Oct 4 20:48:51 UTC 2018 x86_64 ) */ let (_, nii_name_buf) = nfs4_parse_nfsstring(&buf[68..144]).unwrap(); let (_, request) = nfs4_req_exchangeid(&buf[4..]).unwrap(); match request { Nfs4RequestContent::ExchangeId(req_exchangeid) => { assert_eq!(req_exchangeid.client_string, client_string_buf); assert_eq!(req_exchangeid.nii_domain, nii_domain_buf); assert_eq!(req_exchangeid.nii_name, nii_name_buf); } _ => { panic!("Failure, {:?}", request); } } } #[test] fn test_nfs4_request_close() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x04, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*_seq_id*/ 0x00, 0x00, 0x00, 0x01, 0x00, 0x82, 0x14, 0xe0, /*stateid*/ 0x5b, 0x00, 0x88, 0xd9, 0x04, 0x00, 0x00, 0x00, ]; let (_, stateid_buf) = nfs4_parse_stateid(&buf[8..]).unwrap(); let (_, request) = nfs4_req_close(&buf[4..]).unwrap(); match request { Nfs4RequestContent::Close(req_stateid) => { assert_eq!(req_stateid, stateid_buf); } _ => { panic!("Failure, {:?}", request); } } } #[test] fn test_nfs4_request_sequence() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x35, /*opcode*/ 0x00, 0x00, 0x02, 0xd2, 0xe0, 0x14, 0x82, 0x00, /*ssn_id*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x18, /*_seq_id*/ 0x00, 0x00, 0x00, 0x00, /*_slot_id*/ 0x00, 0x00, 0x00, 0x00, /*_high_slot_id*/ 0x00, 0x00, 0x00, 0x01, /*_catch_this*/ ]; let (_, req_sequence) = nfs4_req_sequence(&buf[4..]).unwrap(); match req_sequence { Nfs4RequestContent::Sequence(seq_buf) => { assert_eq!(seq_buf.ssn_id, &buf[4..20]); } _ => { panic!("Failure, {:?}", req_sequence); } } } #[test] fn test_nfs4_request_lookup() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x0f, /*opcode*/ 0x00, 0x00, 0x00, 0x04, 0x76, 0x6f, 0x6c, 0x31, /*filename: (vol1)*/ ]; let (_, filename_buf) = nfs4_parse_nfsstring(&buf[4..]).unwrap(); let (_, request) = nfs4_req_lookup(&buf[4..]).unwrap(); match request { Nfs4RequestContent::Lookup(req_lookup) => { assert_eq!(req_lookup.filename, filename_buf); } _ => { panic!("Failure, {:?}", request); } } } #[test] fn test_nfs4_request_putfh() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x16, /*opcode*/ 0x00, 0x00, 0x00, 0x20, /*handle_len*/ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*handle*/ 0x00, 0x00, 0x00, 0x00, 0x84, 0x72, 0x00, 0x00, 0x23, 0xa6, 0xc0, 0x12, 0x00, 0xf2, 0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let (_, handle_buf) = nfs4_parse_handle(&buf[4..]).unwrap(); let (_, result) = nfs4_req_putfh(&buf[4..]).unwrap(); match result { Nfs4RequestContent::PutFH(putfh_handle) => { assert_eq!(putfh_handle.value, handle_buf.value); assert_eq!(putfh_handle.len, handle_buf.len); } _ => { panic!("Failure, {:?}", result); } } } #[test] fn test_nfs4_request_create_session() { let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x2b, /*opcode*/ 0xe0, 0x14, 0x82, 0x00, 0x00, 0x00, 0x02, 0xd2, // create_session 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x14, 0x00, 0x10, 0x03, 0x88, 0x00, 0x00, 0x0d, 0x64, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x09, 0x5e, 0x92, 0x00, 0x00, 0x00, 0x09, 0x6e, 0x65, 0x74, 0x61, 0x70, 0x70, 0x2d, 0x32, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let (_, request) = nfs4_req_create_session(&buf[4..]).unwrap(); match request { Nfs4RequestContent::CreateSession( create_ssn ) => { assert_eq!(create_ssn.client_id, &buf[4..12]); assert_eq!(create_ssn.seqid, 1); assert_eq!(create_ssn.machine_name, b"netapp-26"); } _ => { panic!("Failure"); } } } #[test] fn test_nfs4_request_layoutget() { let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x32, /*opcode*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // layoutget 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x14, 0xe0, 0x5b, 0x00, 0x89, 0xd9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, ]; let (_, stateid_buf) = nfs4_parse_stateid(&buf[40..56]).unwrap(); assert_eq!(stateid_buf.seqid, 0); let (_, request) = nfs4_req_layoutget(&buf[4..]).unwrap(); match request { Nfs4RequestContent::LayoutGet( lyg_data ) => { assert_eq!(lyg_data.layout_type, 1); assert_eq!(lyg_data.min_length, 4096); assert_eq!(lyg_data.stateid, stateid_buf); } _ => { panic!("Failure"); } } } #[test] fn test_nfs4_request_getdevinfo() { let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x2f, /*opcode*/ 0x01, 0x01, 0x00, 0x00, 0x00, 0xf2, 0xfa, 0x80, // getdevinfo 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x3e, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, ]; let (_, request) = nfs4_req_getdevinfo(&buf[4..]).unwrap(); match request { Nfs4RequestContent::GetDevInfo( getdevifo ) => { assert_eq!(getdevifo.device_id, &buf[4..20]); assert_eq!(getdevifo.layout_type, 1); assert_eq!(getdevifo.maxcount, 81440); assert_eq!(getdevifo.notify_mask, 6); } _ => { panic!("Failure"); } } } #[test] fn test_nfs4_request_layoutreturn() { let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x33, /*opcode*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // layoutreturn 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x03, 0x82, 0x14, 0xe0, 0x5b, 0x00, 0x89, 0xd9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let (_, stateid_buf) = nfs4_parse_stateid(&buf[36..52]).unwrap(); assert_eq!(stateid_buf.seqid, 1); let (_, request) = nfs4_req_layoutreturn(&buf[4..]).unwrap(); match request { Nfs4RequestContent::LayoutReturn( layoutreturn ) => { assert_eq!(layoutreturn.layout_type, 1); assert_eq!(layoutreturn.return_type, 1); assert_eq!(layoutreturn.stateid, stateid_buf); } _ => { panic!("Failure"); } } } #[test] fn test_nfs4_request_destroy_session() { let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x2c, /*opcode*/ 0x00, 0x00, 0x02, 0xd2, 0xe0, 0x14, 0x82, 0x00, /*ssn_id*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, ]; let (_, request) = nfs4_req_destroy_session(&buf[4..]).unwrap(); match request { Nfs4RequestContent::DestroySession( ssn_id ) => { assert_eq!(ssn_id, &buf[4..]); } _ => { panic!("Failure"); } } } #[test] fn test_nfs4_attrs() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x09, /*opcode*/ 0x00, 0x00, 0x00, 0x03, /*attr_cnt*/ 0x00, 0x00, 0x20, 0x65, /*attr_mask[0]*/ 0x00, 0x00, 0x00, 0x00, /*attr_mask[1]*/ 0x00, 0x00, 0x08, 0x00, /*attr_mask[2]*/ ]; let (r, attr) = nfs4_parse_attrbits(&buf[4..]).unwrap(); assert_eq!(r.len(), 0); // assert_eq!(attr.attr_mask, 35618163785728); assert_eq!(attr.attr_mask, 0x00002065_u64 << 32); } #[test] fn test_nfs4_response_compound() { // Operations: SEQUENCE, PUTFH, CLOSE #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x00, /*status*/ 0x00, 0x00, 0x00, 0x00, /*Tag*/ 0x00, 0x00, 0x00, 0x03, /*ops_cnt*/ // SEQUENCE 0x00, 0x00, 0x00, 0x35, /*opcode*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd2, 0xe0, 0x14, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, // PUTFH 0x00, 0x00, 0x00, 0x16, /*opcode*/ 0x00, 0x00, 0x00, 0x00, // CLOSE 0x00, 0x00, 0x00, 0x04, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let sequence_buf: &[u8] = &buf[16..56]; let putfh_buf: &[u8] = &buf[60..64]; let close_buf: &[u8] = &buf[68..]; let (_, res_sequence) = nfs4_res_sequence(sequence_buf).unwrap(); let (_, res_putfh) = nfs4_res_putfh(putfh_buf).unwrap(); let (_, res_close) = nfs4_res_close(close_buf).unwrap(); let (_, compound_ops) = parse_nfs4_response_compound(buf).unwrap(); assert_eq!(compound_ops.status, 0); assert_eq!(compound_ops.commands[0], res_sequence); assert_eq!(compound_ops.commands[1], res_putfh); assert_eq!(compound_ops.commands[2], res_close); } #[test] fn test_nfs4_response_open() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x12, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ // open_data 0x00, 0x00, 0x00, 0x01, 0x00, 0x82, 0x14, 0xe0, /*stateid*/ 0x5b, 0x00, 0x88, 0xd9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x16, 0xf8, 0x2f, 0xd5, /*_change_info*/ 0xdb, 0xb7, 0xfe, 0x38, 0x16, 0xf8, 0x2f, 0xdf, 0x21, 0xa8, 0x2a, 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, /*_attrs*/ 0x00, 0x00, 0x00, 0x00, // delegate_write 0x00, 0x00, 0x00, 0x02, /*delegation_type*/ 0x00, 0x00, 0x00, 0x01, 0x02, 0x82, 0x14, 0xe0, 0x5b, 0x00, 0x89, 0xd9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, ]; let stateid_buf = &buf[8..24]; let (_, res_stateid) = nfs4_parse_stateid(stateid_buf).unwrap(); let delegate_buf = &buf[64..]; let (_, delegate) = nfs4_parse_file_delegation(delegate_buf).unwrap(); let open_data_buf = &buf[8..]; let (_, res_open_data) = nfs4_res_open_ok(open_data_buf).unwrap(); assert_eq!(res_open_data.stateid, res_stateid); assert_eq!(res_open_data.result_flags, 4); assert_eq!(res_open_data.delegate, delegate); let (_, response) = nfs4_res_open(&buf[4..]).unwrap(); match response { Nfs4ResponseContent::Open(status, open_data) => { assert_eq!(status, 0); assert_eq!(open_data, Some(res_open_data)); } _ => { panic!("Failure, {:?}", response); } } } #[test] fn test_nfs4_response_write() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x26, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, /*wd*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]; let (_, wd_buf) = nfs4_res_write_ok(&buf[8..]).unwrap(); assert_eq!(wd_buf.count, 5); assert_eq!(wd_buf.committed, 2); let (_, result) = nfs4_res_write(&buf[4..]).unwrap(); match result { Nfs4ResponseContent::Write(status, wd) => { assert_eq!(status, 0); assert_eq!(wd, Some(wd_buf)); } _ => { panic!("Failure, {:?}", result); } } } #[test] fn test_nfs4_response_access() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x03, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, /*ad*/ ]; let (_, ad_buf) = nfs4_res_access_ok(&buf[8..]).unwrap(); assert_eq!(ad_buf.supported_types, 0x1f); assert_eq!(ad_buf.access_rights, 0x1f); let (_, result) = nfs4_res_access(&buf[4..]).unwrap(); match result { Nfs4ResponseContent::Access(status, ad) => { assert_eq!(status, 0); assert_eq!(ad, Some(ad_buf)); } _ => { panic!("Failure, {:?}", result); } } } #[test] fn test_nfs4_response_getfh() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x0a, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ 0x00, 0x00, 0x00, 0x20, 0x01, 0x01, 0x00, 0x00, /*fh*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x8b, 0xae, 0xea, 0x7f, 0xff, 0xf1, 0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let (_, fh_buf) = nfs4_parse_handle(&buf[8..]).unwrap(); let (_, result) = nfs4_res_getfh(&buf[4..]).unwrap(); match result { Nfs4ResponseContent::GetFH(status, fh) => { assert_eq!(status, 0); assert_eq!(fh, Some(fh_buf)); } _ => { panic!("Failure, {:?}", result); } } } #[test] fn test_nfs4_response_getattr() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x09, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ 0x00, 0x00, 0x00, 0x03, /*attr_cnt*/ 0x00, 0x00, 0x20, 0x65, 0x00, 0x00, 0x00, 0x00, /*attr_mask*/ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, /*attrs*/ 0xfa, 0xfe, 0xbf, 0xff, 0x60, 0xfd, 0xff, 0xfe, 0x00, 0x00, 0x08, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x10, 0x00, 0x00, 0x24, 0x40, 0x32, 0x00, 0x00, 0x00, 0x00 ]; let (_, attrs_buf) = nfs4_parse_attrs(&buf[8..]).unwrap(); let (_, attr_fields) = nfs4_parse_attr_fields(&buf[24..]).unwrap(); assert_eq!(attr_fields, 48); let (_, result) = nfs4_res_getattr(&buf[4..]).unwrap(); match result { Nfs4ResponseContent::GetAttr(status, attrs) => { assert_eq!(status, 0); assert_eq!(attrs, Some(attrs_buf)); } _ => { panic!("Failure, {:?}", result); } } } #[test] fn test_nfs4_response_readdir() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x1a, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*Status: 0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*_verifier*/ // directory_listing 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x55, 0xeb, 0x42, 0x33, /*entry0*/ 0x00, 0x00, 0x00, 0x06, 0x43, 0x65, 0x6e, 0x74, 0x4f, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x18, 0x09, 0x1a, 0x00, 0xb0, 0xa2, 0x3a, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x02, 0xaf, 0x8f, 0x9b, 0x4e, 0x29, 0xc4, 0xa2, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x52, 0x00, 0xb0, 0x33, 0xf7, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x0b, 0x00, 0x00, 0x01, 0xfd, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x12, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x40, 0x66, 0x69, 0x61, 0x6e, 0x65, 0x2e, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x40, 0x66, 0x69, 0x61, 0x6e, 0x65, 0x2e, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x1f, 0x17, 0xbc, 0x28, 0x86, 0x38, 0x31, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x9b, 0x8f, 0xaf, 0x1d, 0xa2, 0xc4, 0x29, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x9b, 0x8f, 0xaf, 0x1d, 0xa2, 0xc4, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, /*entry1*/ 0x00, 0x00, 0x00, 0x04, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x02, 0x00, 0x18, 0x09, 0x1a, 0x00, 0xb0, 0xa2, 0x3a, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x02, 0x83, 0x66, 0x9c, 0x4e, 0x25, 0x80, 0x82, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x52, 0x00, 0xad, 0x37, 0xad, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x02, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x40, 0x66, 0x69, 0x61, 0x6e, 0x65, 0x2e, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x00, 0x00, 0x00, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x40, 0x66, 0x69, 0x61, 0x6e, 0x65, 0x2e, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x6a, 0x97, 0xdb, 0x33, 0x89, 0xba, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x9c, 0x66, 0x83, 0x07, 0x82, 0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x9c, 0x66, 0x83, 0x07, 0x82, 0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, /*value_follows*/ 0x00, 0x00, 0x00, 0x01, /*EOF: YES*/ ]; let entry0_buf = &buf[16..240]; let entry1_buf = &buf[240..452]; let (_, res_entry0) = nfs4_res_readdir_entry_do(&entry0_buf[4..]).unwrap(); assert_eq!(res_entry0.name, "CentOS".as_bytes()); let (_, res_entry1) = nfs4_res_readdir_entry_do(&entry1_buf[4..]).unwrap(); assert_eq!(res_entry1.name, "data".as_bytes()); let (_, res_rd) = nfs4_res_readdir_ok(&buf[8..]).unwrap(); assert!(res_rd.eof); assert_eq!(res_rd.listing, [Some(res_entry0), Some(res_entry1)]); let (_, response) = nfs4_res_readdir(&buf[4..]).unwrap(); match response { Nfs4ResponseContent::ReadDir(status, rd) => { assert_eq!(status, 0); assert_eq!(rd, Some(res_rd)); } _ => { panic!("Failure!"); } } } #[test] fn test_nfs4_response_setclientid() { #[rustfmt::skip] let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x23, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ 0x14, 0x67, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, /*_clientid*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*_verifier*/ ]; let (_, response) = nfs4_res_setclientid(&buf[4..]).unwrap(); match response { Nfs4ResponseContent::SetClientId(status) => { assert_eq!(status, 0); } _ => { panic!("Failure"); } } } #[test] fn test_nfs4_response_exchangeid() { let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x2a, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ // exchange_id 0xe0, 0x14, 0x82, 0x00, 0x00, 0x00, 0x02, 0xd2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x3b, 0xa3, 0x1e, 0xd7, 0xa9, 0x11, 0xe8, 0x00, 0x00, 0x00, 0x10, 0x98, 0x3b, 0xa3, 0x1e, 0xd7, 0xa9, 0x11, 0xe8, 0xbc, 0x0c, 0x00, 0x0c, 0x29, 0xe9, 0x13, 0x93, 0x00, 0x00, 0x00, 0x10, 0x84, 0x8b, 0x93, 0x12, 0xd7, 0xa9, 0x11, 0xe8, 0xbc, 0x0c, 0x00, 0x0c, 0x29, 0xe9, 0x13, 0x93, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x6e, 0x65, 0x74, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x4e, 0x65, 0x74, 0x41, 0x70, 0x70, 0x20, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x56, 0x6f, 0x6f, 0x64, 0x6f, 0x6f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x5f, 0x5f, 0x39, 0x2e, 0x36, 0x2e, 0x30, 0x00, 0x00, 0x26, 0x0d, 0xcf, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let (_, xchangeid) = nfs4_parse_res_exchangeid(&buf[8..]).unwrap(); let (_, response) = nfs4_res_exchangeid(&buf[4..]).unwrap(); match response { Nfs4ResponseContent::ExchangeId(status, xchngid_data) => { assert_eq!(status, 0); assert_eq!(xchngid_data, Some(xchangeid)); } _ => { panic!("Failure"); } } } #[test] fn test_nfs4_response_create_session() { let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x2b, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ // create_session 0x00, 0x00, 0x02, 0xd2, 0xe0, 0x14, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, ]; let (_, create_ssn) = nfs4_parse_res_create_session(&buf[8..]).unwrap(); let (_, response) = nfs4_res_create_session(&buf[4..]).unwrap(); match response { Nfs4ResponseContent::CreateSession( status, create_ssn_data) => { assert_eq!(status, 0); assert_eq!(create_ssn_data, Some(create_ssn)); } _ => { panic!("Failure"); } } } #[test] fn test_nfs4_response_layoutget() { let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x32, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ // layoutget 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x82, 0x14, 0xe0, 0x5b, 0x00, 0x89, 0xd9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0x01, 0x01, 0x00, 0x00, 0x00, 0xf2, 0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x72, 0x00, 0x00, 0x23, 0xa6, 0xc0, 0x12, 0x00, 0xf2, 0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xf2, 0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, ]; let (_, stateid) = nfs4_parse_stateid(&buf[12..28]).unwrap(); let (_, lyg_data) = nfs4_parse_res_layoutget(&buf[8..]).unwrap(); assert_eq!(lyg_data.stateid, stateid); assert_eq!(lyg_data.layout_type, 1); assert_eq!(lyg_data.device_id, &buf[60..76]); let (_, response) = nfs4_res_layoutget(&buf[4..]).unwrap(); match response { Nfs4ResponseContent::LayoutGet( status, lyg ) => { assert_eq!(status, 0); assert_eq!(lyg, Some(lyg_data)); } _ => { panic!("Failure"); } } } #[test] fn test_nfs4_response_getdevinfo() { let buf: &[u8] = &[ 0x00, 0x00, 0x00, 0x2f, /*opcode*/ 0x00, 0x00, 0x00, 0x00, /*status*/ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, // getdevinfo 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x74, 0x63, 0x70, 0x00, 0x00, 0x00, 0x00, 0x10, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x30, 0x2e, 0x36, 0x31, 0x2e, 0x38, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, ]; let (_, getdevinfo) = nfs4_parse_res_getdevinfo(&buf[8..]).unwrap(); assert_eq!(getdevinfo.layout_type, 1); assert_eq!(getdevinfo.r_netid, b"tcp"); assert_eq!(getdevinfo.r_addr, b"192.168.0.61.8.1"); let (_, response) = nfs4_res_getdevinfo(&buf[4..]).unwrap(); match response { Nfs4ResponseContent::GetDevInfo(status, getdevinfo_data) => { assert_eq!(status, 0); assert_eq!(getdevinfo_data, Some(getdevinfo)) } _ => { panic!("Failure"); } } } }