]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
nfs4_records: add unittests for nom7 parsers
authorSam Muhammed <ghostinthehive.vx@gmail.com>
Fri, 7 Jan 2022 17:27:06 +0000 (19:27 +0200)
committerVictor Julien <vjulien@oisf.net>
Thu, 13 Jan 2022 07:16:23 +0000 (08:16 +0100)
Task #4866

rust/src/nfs/nfs4_records.rs

index 01929215c4249318f1ac1184e379470ea93fa37d..d8950354e6bb2d42125a6da8feaba314ce272cac 100644 (file)
@@ -830,3 +830,462 @@ pub fn parse_nfs4_response_compound(i: &[u8]) -> IResult<&[u8], Nfs4ResponseComp
     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_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_sequenece() {
+        #[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, /*fiename: (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_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 as u64) << 32 | 0 as u64));
+    }
+    #[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, /*result_flags*/
+            0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, /*_attrs*/
+            0x00, 0x00, 0x00, 0x02, /*delegation_type*/
+        // delegate_read
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+            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 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.delegation_type, 2);
+        assert_eq!(res_open_data.delegate_read, None);
+
+        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); }
+        }
+    }
+}