onc_rpc/
rpc_message.rs

1//! Contains types to implement the Open Network Computing RPC specification
2//! defined in RFC 5531
3
4use std::{
5    convert::TryFrom,
6    io::{Cursor, Write},
7};
8
9use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
10
11use crate::{reply::ReplyBody, CallBody, Error};
12
13const MSG_HEADER_LEN: usize = 4;
14const LAST_FRAGMENT_BIT: u32 = 1 << 31;
15
16const MESSAGE_TYPE_CALL: u32 = 0;
17const MESSAGE_TYPE_REPLY: u32 = 1;
18
19// TODO: serialise_ioslice() -> IoSliceBuffer
20
21/// The type of RPC message.
22#[derive(Debug, PartialEq)]
23pub enum MessageType<T, P>
24where
25    T: AsRef<[u8]>,
26    P: AsRef<[u8]>,
27{
28    /// This message is invoking an RPC.
29    Call(CallBody<T, P>),
30    /// This message is a response to an RPC request.
31    Reply(ReplyBody<T, P>),
32}
33
34impl<'a> MessageType<&'a [u8], &'a [u8]> {
35    /// Constructs a new `MessageType` by parsing the wire format read from `r`.
36    ///
37    /// `from_cursor` advances the position of `r` to the end of the
38    /// `MessageType` structure.
39    pub(crate) fn from_cursor(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
40        match r.read_u32::<BigEndian>()? {
41            MESSAGE_TYPE_CALL => Ok(MessageType::Call(CallBody::from_cursor(r)?)),
42            MESSAGE_TYPE_REPLY => Ok(MessageType::Reply(ReplyBody::from_cursor(r)?)),
43            v => Err(Error::InvalidMessageType(v)),
44        }
45    }
46}
47
48impl<T, P> MessageType<T, P>
49where
50    T: AsRef<[u8]>,
51    P: AsRef<[u8]>,
52{
53    /// Serialises this `MessageType` into `buf`, advancing the cursor position
54    /// by [`MessageType::serialised_len()`] bytes.
55    pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
56        match self {
57            Self::Call(b) => {
58                buf.write_u32::<BigEndian>(MESSAGE_TYPE_CALL)?;
59                b.serialise_into(buf)?;
60            }
61            Self::Reply(b) => {
62                buf.write_u32::<BigEndian>(MESSAGE_TYPE_REPLY)?;
63                b.serialise_into(buf)?;
64            }
65        }
66
67        Ok(())
68    }
69
70    /// Returns the on-wire length of this message once serialised, including
71    /// the message header.
72    pub fn serialised_len(&self) -> u32 {
73        match self {
74            Self::Call(c) => c.serialised_len() + 4,
75            Self::Reply(r) => r.serialised_len() + 4,
76        }
77    }
78}
79
80#[cfg(feature = "bytes")]
81impl TryFrom<crate::Bytes> for MessageType<crate::Bytes, crate::Bytes> {
82    type Error = Error;
83
84    fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
85        use crate::bytes_ext::BytesReaderExt;
86
87        match v.try_u32()? {
88            MESSAGE_TYPE_CALL => Ok(Self::Call(CallBody::try_from(v)?)),
89            MESSAGE_TYPE_REPLY => Ok(Self::Reply(ReplyBody::try_from(v)?)),
90            v => Err(Error::InvalidMessageType(v)),
91        }
92    }
93}
94
95/// An Open Network Computing RPC message, generic over a source of bytes (`T`)
96/// and a payload buffer (`P`).
97#[derive(Debug, PartialEq)]
98pub struct RpcMessage<T, P>
99where
100    T: AsRef<[u8]>,
101    P: AsRef<[u8]>,
102{
103    xid: u32,
104    message_type: MessageType<T, P>,
105}
106
107impl<'a> RpcMessage<&'a [u8], &'a [u8]> {
108    /// Deserialises a new [`RpcMessage`] from `buf`.
109    ///
110    /// Buf must contain exactly 1 message - if `buf` contains an incomplete
111    /// message, or `buf` contains trailing bytes after the message
112    /// [`Error::IncompleteMessage`] is returned.
113    #[deprecated(since = "0.3.0", note = "prefer the `TryFrom` impl instead")]
114    pub fn from_bytes(buf: &'a [u8]) -> Result<Self, Error> {
115        Self::try_from(buf)
116    }
117}
118
119impl<T, P> RpcMessage<T, P>
120where
121    T: AsRef<[u8]>,
122    P: AsRef<[u8]>,
123{
124    /// Construct a new `RpcMessage` with the specified transaction ID and
125    /// message body.
126    pub fn new(xid: u32, message_type: MessageType<T, P>) -> Self {
127        Self { xid, message_type }
128    }
129
130    /// Write this `RpcMessage` into `buf`, advancing the cursor to the end of
131    /// the serialised message. `buf` must have capacity for at least
132    /// [`RpcMessage::serialised_len()`] bytes from the current cursor position.
133    ///
134    /// This method allows the caller to specify the underlying buffer used to
135    /// hold the serialised message to enable reuse and pooling.
136    pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
137        use std::io;
138
139        // Build the message header.
140        //
141        // The header is a 31 bit number describing the length, and a "this is
142        // the last fragment" flag.
143        //
144        // Because of this, serialised messages cannot exceed (2^31 - 1) bytes
145        // (basically, the length cannot have the MSB set).
146        if self.serialised_len() & LAST_FRAGMENT_BIT != 0 {
147            return Err(io::Error::new(
148                io::ErrorKind::InvalidInput,
149                "message length exceeds maximum",
150            ));
151        }
152
153        // Write the header.
154        //
155        // The header length (4 bytes) is not included in message length value.
156        let header = (self.serialised_len() - 4) | LAST_FRAGMENT_BIT;
157        buf.write_u32::<BigEndian>(header)?;
158
159        // Write the XID
160        buf.write_u32::<BigEndian>(self.xid)?;
161
162        // Write the message body
163        self.message_type.serialise_into(buf)
164    }
165
166    /// Serialise this `RpcMessage` into a new [`Vec`].
167    ///
168    /// The returned vec will be sized exactly to contain this message. Calling
169    /// this method is the equivalent of:
170    ///
171    /// ```
172    /// # use onc_rpc::{*, auth::*};
173    /// # use std::io::Cursor;
174    /// # let payload = vec![];
175    /// # let msg = RpcMessage::<&[u8], &[u8]>::new(
176    /// #     4242,
177    /// #     MessageType::Call(CallBody::new(
178    /// #         100000,
179    /// #         42,
180    /// #         13,
181    /// #         AuthFlavor::AuthNone(None),
182    /// #         AuthFlavor::AuthNone(None),
183    /// #         &payload,
184    /// #     )),
185    /// # );
186    /// #
187    /// let mut buf = Vec::with_capacity(msg.serialised_len() as usize);
188    /// let mut c = Cursor::new(buf);
189    /// msg.serialise_into(&mut c);
190    /// ```
191    ///
192    /// [`Vec`]: std::vec::Vec
193    pub fn serialise(&self) -> Result<Vec<u8>, std::io::Error> {
194        let mut buf = Cursor::new(Vec::with_capacity(self.serialised_len() as usize));
195        self.serialise_into(&mut buf)?;
196        Ok(buf.into_inner())
197    }
198
199    /// Returns the on-wire length of this message once serialised, including
200    /// the message header.
201    pub fn serialised_len(&self) -> u32 {
202        // +4 for xid, +4 for header
203        self.message_type.serialised_len() + 4 + 4
204    }
205
206    /// The transaction ID for this request.
207    pub fn xid(&self) -> u32 {
208        self.xid
209    }
210
211    /// The [`MessageType`] contained in this request.
212    pub fn message(&self) -> &MessageType<T, P> {
213        &self.message_type
214    }
215
216    /// Returns the [`CallBody`] in this request, or `None` if this message is
217    /// not a RPC call request.
218    pub fn call_body(&self) -> Option<&CallBody<T, P>> {
219        match self.message_type {
220            MessageType::Call(ref b) => Some(b),
221            _ => None,
222        }
223    }
224
225    /// Returns the [`ReplyBody`] in this request, or `None` if this message is
226    /// not a RPC response.
227    pub fn reply_body(&self) -> Option<&ReplyBody<T, P>> {
228        match self.message_type {
229            MessageType::Reply(ref b) => Some(b),
230            _ => None,
231        }
232    }
233}
234
235impl<'a> TryFrom<&'a [u8]> for RpcMessage<&'a [u8], &'a [u8]> {
236    type Error = Error;
237
238    /// Deserialises a new [`RpcMessage`] from `buf`.
239    ///
240    /// Buf must contain exactly 1 message - if `buf` contains an incomplete
241    /// message, or `buf` contains trailing bytes after the message
242    /// [`Error::IncompleteMessage`] is returned.
243    fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
244        // Unwrap the message header, validating the length of data.
245        let data = unwrap_header(v)?;
246
247        // Wrap the data in a cursor for ease of parsing.
248        let mut r = Cursor::new(data);
249
250        let xid = r.read_u32::<BigEndian>()?;
251        let message_type = MessageType::from_cursor(&mut r)?;
252
253        let msg = RpcMessage { xid, message_type };
254
255        // Detect messages that have more data than what was deserialised.
256        //
257        // This can occur if a message has a valid header length value for data,
258        // but data contains more bytes than expected for this message type.
259        //
260        // +4 for the header which was
261        let want_len = v.len() as u32;
262        if msg.serialised_len() != want_len {
263            return Err(Error::IncompleteMessage {
264                buffer_len: v.len(),
265                expected: msg.serialised_len() as usize,
266            });
267        }
268
269        Ok(msg)
270    }
271}
272
273#[cfg(feature = "bytes")]
274impl TryFrom<crate::Bytes> for RpcMessage<crate::Bytes, crate::Bytes> {
275    type Error = Error;
276
277    fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
278        use crate::{bytes_ext::BytesReaderExt, Buf};
279
280        let original_buffer_len = v.len();
281
282        // Read the message length from the header, and check v contains exactly
283        // one message.
284        let want = expected_message_len(v.as_ref())? as usize;
285        if original_buffer_len != want {
286            return Err(Error::IncompleteMessage {
287                buffer_len: original_buffer_len,
288                expected: want,
289            });
290        }
291
292        // Advance past the header bytes
293        v.advance(MSG_HEADER_LEN);
294
295        let xid = v.try_u32()?;
296        let message_type = MessageType::try_from(v)?;
297
298        let msg = Self { xid, message_type };
299
300        // Detect messages that have more data than what was deserialised.
301        //
302        // This can occur if a message has a valid header length value for data,
303        // but data contains more bytes than expected for this message type.
304        let parsed_len = msg.serialised_len() as usize;
305        if parsed_len != original_buffer_len {
306            return Err(Error::IncompleteMessage {
307                buffer_len: original_buffer_len,
308                expected: parsed_len,
309            });
310        }
311
312        Ok(msg)
313    }
314}
315
316/// Strip the 4 byte header from data, returning the rest of the message.
317///
318/// This function validates the message length value in the header matches the
319/// length of `data`, and ensures this is not a fragmented message.
320fn unwrap_header(data: &[u8]) -> Result<&[u8], Error> {
321    let want = expected_message_len(data)?;
322
323    // Validate the buffer contains the specified amount of data after the
324    // header.
325    let remaining_data = &data[MSG_HEADER_LEN..];
326
327    if data.len() != want as usize {
328        return Err(Error::IncompleteMessage {
329            buffer_len: data.len(),
330            expected: want as usize,
331        });
332    }
333
334    Ok(remaining_data)
335}
336
337/// Reads the message header from data, and returns the expected wire length of
338/// the RPC message.
339///
340/// `data` must contain at least 4 bytes, and must be the start of an RPC
341/// message for this call to return valid data. If the message does not have the
342/// `last fragment` bit set, [`Error::Fragmented`] is returned.
343pub fn expected_message_len(data: &[u8]) -> Result<u32, Error> {
344    if data.len() < MSG_HEADER_LEN {
345        return Err(Error::IncompleteHeader);
346    }
347
348    // Read the 4 byte fragment header.
349    //
350    // RFC1831 defines it as a big endian, 4 byte unsigned number:
351    //
352    // > The number encodes two values -- a boolean which indicates whether the
353    // > fragment is the last fragment of the record (bit value 1 implies the
354    // > fragment is the last fragment) and a 31-bit unsigned binary value which is
355    // > the length in bytes of the fragment's data.  The boolean value is the
356    // > highest-order bit of the header; the length is the 31 low-order bits.
357    //
358    let header = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
359
360    // Ensure the "last fragment" bit is set
361    if header & LAST_FRAGMENT_BIT == 0 {
362        return Err(Error::Fragmented);
363    }
364
365    // +4 for the header bytes not counted in the "length" value.
366    Ok((header & !LAST_FRAGMENT_BIT) + 4)
367}
368
369#[cfg(test)]
370mod tests {
371    use crate::Opaque;
372    use std::ops::RangeInclusive;
373
374    use hex_literal::hex;
375    use proptest::{option::of, prelude::*, sample::SizeRange};
376
377    #[cfg(feature = "bytes")]
378    use crate::Bytes;
379
380    use super::*;
381    use crate::{
382        auth::{AuthFlavor, AuthUnixParams},
383        AcceptedReply, AcceptedStatus, AuthError, RejectedReply,
384    };
385
386    #[test]
387    fn test_unwrap_header() {
388        let x = hex!(
389            "80 00 01 1c 265ec0fd0000000000000002000186a30000000400000001000000
390			01000000540000000000000000000001f50000001400000010000001f50000000c0
391			00000140000003d0000004f000000500000005100000062000002bd000000210000
392			0064000000cc000000fa0000018b0000018e0000018f00000000000000000000000
393			c736574636c696420202020200000000000000001000000235ed267a20000683900
394			00004b00000000f8ffc247f4fb10020801c0a801bd00000000000000003139322e3
395			136382e312e3138393a2f686f6d652f646f6d002f55736572732f646f6d2f446573
396			6b746f702f6d6f756e7400004e4653430000000374637000000000153139322e313
397			6382e312e3138382e3233382e32333500000000000002"
398        );
399
400        let want = &x[4..];
401
402        assert_eq!(unwrap_header(&x), Ok(want));
403    }
404
405    #[test]
406    fn test_unwrap_header_validates_expected() {
407        let x = hex!("80");
408
409        assert_eq!(unwrap_header(&x).unwrap_err(), Error::IncompleteHeader);
410    }
411
412    #[test]
413    fn test_unwrap_header_validates_message_len() {
414        let x = hex!("80 00 01 1c 265ec0fd0000000000000002");
415
416        assert_eq!(
417            unwrap_header(&x),
418            Err(Error::IncompleteMessage {
419                buffer_len: 16,
420                expected: 288,
421            })
422        );
423    }
424
425    #[test]
426    fn test_unwrap_header_validates_fragment_bit() {
427        let x = hex!("00 00 01 1c 265ec0fd0000000000000002");
428
429        assert_eq!(unwrap_header(&x), Err(Error::Fragmented));
430    }
431
432    // A compile-time test that ensures a payload can differ in type from the
433    // auth buffer.
434    #[test]
435    fn test_differing_payload_type() {
436        let binding = vec![42];
437        let auth = AuthFlavor::AuthNone(Some(binding.as_slice()));
438        let payload = [42, 42, 42, 42];
439
440        let _msg: RpcMessage<&[u8], &[u8; 4]> = RpcMessage::new(
441            4242,
442            MessageType::Call(CallBody::new(100000, 42, 13, auth.clone(), auth, &payload)),
443        );
444    }
445
446    #[test]
447    fn test_rpcmessage_auth_unix() {
448        // Frame 3: 354 bytes on wire (2832 bits), 354 bytes captured (2832 bits) on interface en0, id 0
449        // Ethernet II, Src: Apple_47:f4:fb (f8:ff:c2:47:f4:fb), Dst: PcsCompu_76:48:20 (08:00:27:76:48:20)
450        // Internet Protocol Version 4, Src: client (192.168.1.188), Dst: server (192.168.1.189)
451        // Transmission Control Protocol, Src Port: 61162, Dst Port: 2049, Seq: 69, Ack: 29, Len: 288
452        // Remote Procedure Call, Type:Call XID:0x265ec0fd
453        //     Fragment header: Last fragment, 284 bytes
454        //         1... .... .... .... .... .... .... .... = Last Fragment: Yes
455        //         .000 0000 0000 0000 0000 0001 0001 1100 = Fragment Length: 284
456        //     XID: 0x265ec0fd (643743997)
457        //     Message Type: Call (0)
458        //     RPC Version: 2
459        //     Program: NFS (100003)
460        //     Program Version: 4
461        //     Procedure: COMPOUND (1)
462        //     [The reply to this request is in frame 4]
463        //     Credentials
464        //         Flavor: AUTH_UNIX (1)
465        //         Length: 84
466
467        //         Stamp: 0x00000000
468        //         Machine Name: <EMPTY>
469        //             length: 0
470        //             contents: <EMPTY>
471        //         UID: 501
472        //         GID: 20
473        //         Auxiliary GIDs (16) [501, 12, 20, 61, 79, 80, 81, 98, 701, 33, 100, 204, 250, 395, 398, 399]
474        //             GID: 501
475        //             GID: 12
476        //             GID: 20
477        //             GID: 61
478        //             GID: 79
479        //             GID: 80
480        //             GID: 81
481        //             GID: 98
482        //             GID: 701
483        //             GID: 33
484        //             GID: 100
485        //             GID: 204
486        //             GID: 250
487        //             GID: 395
488        //             GID: 398
489        //             GID: 399
490
491        //     Verifier
492        //         Flavor: AUTH_NULL (0)
493        //         Length: 0
494        // Network File System, Ops(1): SETCLIENTID
495        //     [Program Version: 4]
496        //     [V4 Procedure: COMPOUND (1)]
497        //     Tag: setclid
498        //         length: 12
499        //         contents: setclid
500        //     minorversion: 0
501        //     Operations (count: 1): SETCLIENTID
502        //         Opcode: SETCLIENTID (35)
503        //             client
504        //                 verifier: 0x5ed267a200006839
505        //                 id: <DATA>
506        //                     length: 75
507        //                     contents: <DATA>
508        //                     fill bytes: opaque data
509        //             callback
510        //                 cb_program: 0x4e465343
511        //                 cb_location
512        //                     r_netid: tcp
513        //                         length: 3
514        //                         contents: tcp
515        //                         fill bytes: opaque data
516        //                     r_addr: 192.168.1.188.238.235
517        //                         length: 21
518        //                         contents: 192.168.1.188.238.235
519        //                         fill bytes: opaque data
520        //                     [IPv4 address 192.168.1.188, protocol=tcp, port=61163]
521        //             callback_ident: 0x00000002
522        //     [Main Opcode: SETCLIENTID (35)]
523
524        const RAW: [u8; 288] = hex!(
525            "8000011c265ec0fd0000000000000002000186a300000004000000010000000100
526			0000540000000000000000000001f50000001400000010000001f50000000c00000
527			0140000003d0000004f000000500000005100000062000002bd0000002100000064
528			000000cc000000fa0000018b0000018e0000018f00000000000000000000000c736
529			574636c696420202020200000000000000001000000235ed267a200006839000000
530			4b00000000f8ffc247f4fb10020801c0a801bd00000000000000003139322e31363
531			82e312e3138393a2f686f6d652f646f6d002f55736572732f646f6d2f4465736b74
532			6f702f6d6f756e7400004e4653430000000374637000000000153139322e3136382
533			e312e3138382e3233382e32333500000000000002"
534        );
535
536        assert_eq!(expected_message_len(RAW.as_ref()).unwrap(), 288);
537
538        let msg = RpcMessage::try_from(RAW.as_ref()).expect("failed to parse message");
539        assert_eq!(msg.xid(), 643743997);
540        assert_eq!(msg.serialised_len(), 288);
541
542        let body = msg.call_body().expect("not a call rpc");
543        assert_eq!(body.rpc_version(), 2);
544        assert_eq!(body.program(), 100003);
545        assert_eq!(body.program_version(), 4);
546        assert_eq!(body.procedure(), 1);
547
548        assert_eq!(body.auth_credentials().serialised_len(), 92);
549        let auth = match *body.auth_credentials() {
550            AuthFlavor::AuthUnix(ref v) => v,
551            _ => panic!("unexpected auth type"),
552        };
553
554        assert_eq!(auth.stamp(), 0x00000000);
555        assert_eq!(auth.machine_name_str(), "");
556        assert_eq!(auth.uid(), 501);
557        assert_eq!(auth.gid(), 20);
558        assert_eq!(
559            auth.gids(),
560            Some(
561                [501, 12, 20, 61, 79, 80, 81, 98, 701, 33, 100, 204, 250, 395, 398, 399].as_slice()
562            )
563        );
564        assert_eq!(auth.serialised_len(), 84);
565
566        assert_eq!(*body.auth_verifier(), AuthFlavor::AuthNone(None));
567
568        let payload = hex!(
569            "0000000c736574636c696420202020200000000000000001000000235ed267a200
570			0068390000004b00000000f8ffc247f4fb10020801c0a801bd00000000000000003
571			139322e3136382e312e3138393a2f686f6d652f646f6d002f55736572732f646f6d
572			2f4465736b746f702f6d6f756e7400004e465343000000037463700000000015313
573			9322e3136382e312e3138382e3233382e32333500000000000002"
574        );
575
576        assert_eq!(body.payload().as_ref(), payload.as_ref());
577
578        let serialised = msg.serialise().expect("failed to serialise");
579        assert_eq!(serialised.as_slice(), RAW.as_ref());
580    }
581
582    #[test]
583    #[cfg(feature = "bytes")]
584    fn test_rpcmessage_auth_unix_bytes() {
585        // Frame 3: 354 bytes on wire (2832 bits), 354 bytes captured (2832 bits) on interface en0, id 0
586        // Ethernet II, Src: Apple_47:f4:fb (f8:ff:c2:47:f4:fb), Dst: PcsCompu_76:48:20 (08:00:27:76:48:20)
587        // Internet Protocol Version 4, Src: client (192.168.1.188), Dst: server (192.168.1.189)
588        // Transmission Control Protocol, Src Port: 61162, Dst Port: 2049, Seq: 69, Ack: 29, Len: 288
589        // Remote Procedure Call, Type:Call XID:0x265ec0fd
590        //     Fragment header: Last fragment, 284 bytes
591        //         1... .... .... .... .... .... .... .... = Last Fragment: Yes
592        //         .000 0000 0000 0000 0000 0001 0001 1100 = Fragment Length: 284
593        //     XID: 0x265ec0fd (643743997)
594        //     Message Type: Call (0)
595        //     RPC Version: 2
596        //     Program: NFS (100003)
597        //     Program Version: 4
598        //     Procedure: COMPOUND (1)
599        //     [The reply to this request is in frame 4]
600        //     Credentials
601        //         Flavor: AUTH_UNIX (1)
602        //         Length: 84
603
604        //         Stamp: 0x00000000
605        //         Machine Name: <EMPTY>
606        //             length: 0
607        //             contents: <EMPTY>
608        //         UID: 501
609        //         GID: 20
610        //         Auxiliary GIDs (16) [501, 12, 20, 61, 79, 80, 81, 98, 701, 33, 100, 204, 250, 395, 398, 399]
611        //             GID: 501
612        //             GID: 12
613        //             GID: 20
614        //             GID: 61
615        //             GID: 79
616        //             GID: 80
617        //             GID: 81
618        //             GID: 98
619        //             GID: 701
620        //             GID: 33
621        //             GID: 100
622        //             GID: 204
623        //             GID: 250
624        //             GID: 395
625        //             GID: 398
626        //             GID: 399
627
628        //     Verifier
629        //         Flavor: AUTH_NULL (0)
630        //         Length: 0
631        // Network File System, Ops(1): SETCLIENTID
632        //     [Program Version: 4]
633        //     [V4 Procedure: COMPOUND (1)]
634        //     Tag: setclid
635        //         length: 12
636        //         contents: setclid
637        //     minorversion: 0
638        //     Operations (count: 1): SETCLIENTID
639        //         Opcode: SETCLIENTID (35)
640        //             client
641        //                 verifier: 0x5ed267a200006839
642        //                 id: <DATA>
643        //                     length: 75
644        //                     contents: <DATA>
645        //                     fill bytes: opaque data
646        //             callback
647        //                 cb_program: 0x4e465343
648        //                 cb_location
649        //                     r_netid: tcp
650        //                         length: 3
651        //                         contents: tcp
652        //                         fill bytes: opaque data
653        //                     r_addr: 192.168.1.188.238.235
654        //                         length: 21
655        //                         contents: 192.168.1.188.238.235
656        //                         fill bytes: opaque data
657        //                     [IPv4 address 192.168.1.188, protocol=tcp, port=61163]
658        //             callback_ident: 0x00000002
659        //     [Main Opcode: SETCLIENTID (35)]
660
661        const RAW: [u8; 288] = hex!(
662            "8000011c265ec0fd0000000000000002000186a300000004000000010000000100
663    		0000540000000000000000000001f50000001400000010000001f50000000c00000
664    		0140000003d0000004f000000500000005100000062000002bd0000002100000064
665    		000000cc000000fa0000018b0000018e0000018f00000000000000000000000c736
666    		574636c696420202020200000000000000001000000235ed267a200006839000000
667    		4b00000000f8ffc247f4fb10020801c0a801bd00000000000000003139322e31363
668    		82e312e3138393a2f686f6d652f646f6d002f55736572732f646f6d2f4465736b74
669    		6f702f6d6f756e7400004e4653430000000374637000000000153139322e3136382
670    		e312e3138382e3233382e32333500000000000002"
671        );
672
673        let static_raw: &'static [u8] = Box::leak(Box::new(RAW));
674
675        assert_eq!(expected_message_len(static_raw).unwrap(), 288);
676
677        let msg = RpcMessage::try_from(Bytes::from(static_raw)).expect("failed to parse message");
678        assert_eq!(msg.xid(), 643743997);
679        assert_eq!(msg.serialised_len(), 288);
680
681        let body = msg.call_body().expect("not a call rpc");
682        assert_eq!(body.rpc_version(), 2);
683        assert_eq!(body.program(), 100003);
684        assert_eq!(body.program_version(), 4);
685        assert_eq!(body.procedure(), 1);
686
687        assert_eq!(body.auth_credentials().serialised_len(), 92);
688        let auth = match body.auth_credentials() {
689            AuthFlavor::AuthUnix(ref v) => v,
690            v => panic!("unexpected auth type {:?}", v),
691        };
692
693        assert_eq!(auth.stamp(), 0x00000000);
694        assert_eq!(auth.machine_name_str(), "");
695        assert_eq!(auth.uid(), 501);
696        assert_eq!(auth.gid(), 20);
697        assert_eq!(
698            auth.gids(),
699            Some(
700                [501, 12, 20, 61, 79, 80, 81, 98, 701, 33, 100, 204, 250, 395, 398, 399].as_slice()
701            )
702        );
703        assert_eq!(auth.serialised_len(), 84);
704
705        assert_eq!(*body.auth_verifier(), AuthFlavor::AuthNone(None));
706
707        let payload = hex!(
708            "0000000c736574636c696420202020200000000000000001000000235ed267a200
709    		0068390000004b00000000f8ffc247f4fb10020801c0a801bd00000000000000003
710    		139322e3136382e312e3138393a2f686f6d652f646f6d002f55736572732f646f6d
711    		2f4465736b746f702f6d6f756e7400004e465343000000037463700000000015313
712    		9322e3136382e312e3138382e3233382e32333500000000000002"
713        );
714
715        assert_eq!(body.payload(), payload.as_ref());
716
717        let serialised = msg.serialise().expect("failed to serialise");
718        assert_eq!(serialised.as_slice(), RAW.as_ref());
719    }
720
721    #[test]
722    fn test_rpcmessage_auth_unix_empty<'a>() {
723        // Remote Procedure Call, Type:Call XID:0x265ec106
724        //     Fragment header: Last fragment, 152 bytes
725        //         1... .... .... .... .... .... .... .... = Last Fragment: Yes
726        //         .000 0000 0000 0000 0000 0000 1001 1000 = Fragment Length: 152
727        //     XID: 0x265ec106 (643744006)
728        //     Message Type: Call (0)
729        //     RPC Version: 2
730        //     Program: NFS (100003)
731        //     Program Version: 4
732        //     Procedure: COMPOUND (1)
733        //     [The reply to this request is in frame 22]
734        //     Credentials
735        //         Flavor: AUTH_UNIX (1)
736        //         Length: 24
737        //         Stamp: 0x00000000
738        //         Machine Name: <EMPTY>
739        //             length: 0
740        //             contents: <EMPTY>
741        //         UID: 0
742        //         GID: 0
743        //         Auxiliary GIDs (1) [0]
744        //             GID: 0
745        //     Verifier
746        //         Flavor: AUTH_NULL (0)
747        //         Length: 0
748        // Network File System, Ops(3): PUTFH, ACCESS, GETATTR
749        //     [Program Version: 4]
750        //     [V4 Procedure: COMPOUND (1)]
751        //     Tag: access
752        //         length: 12
753        //         contents: access
754        //     minorversion: 0
755        //     Operations (count: 3): PUTFH, ACCESS, GETATTR
756        //         Opcode: PUTFH (22)
757        //             FileHandle
758        //                 length: 31
759        //                 [hash (CRC-32): 0x4bcbccda]
760        //                 FileHandle: 4300004d1a436f6c452240ea4c70a1b52d7f97418e6601a1…
761        //         Opcode: ACCESS (3), [Check: RD LU MD XT DL XE]
762        //             Check access: 0x3f
763        //                 .... ...1 = 0x001 READ: allowed?
764        //                 .... ..1. = 0x002 LOOKUP: allowed?
765        //                 .... .1.. = 0x004 MODIFY: allowed?
766        //                 .... 1... = 0x008 EXTEND: allowed?
767        //                 ...1 .... = 0x010 DELETE: allowed?
768        //                 ..1. .... = 0x020 EXECUTE: allowed?
769        //         Opcode: GETATTR (9)
770        //             Attr mask[0]: 0x1010011a (Type, Change, Size, FSID, FileId, MaxLink)
771        //                 reqd_attr: Type (1)
772        //                 reqd_attr: Change (3)
773        //                 reqd_attr: Size (4)
774        //                 reqd_attr: FSID (8)
775        //                 reco_attr: FileId (20)
776        //                 reco_attr: MaxLink (28)
777        //             Attr mask[1]: 0x00b0a23a (Mode, NumLinks, Owner, Owner_Group, RawDev, Space_Used, Time_Access, Time_Metadata, Time_Modify, Mounted_on_FileId)
778        //                 reco_attr: Mode (33)
779        //                 reco_attr: NumLinks (35)
780        //                 reco_attr: Owner (36)
781        //                 reco_attr: Owner_Group (37)
782        //                 reco_attr: RawDev (41)
783        //                 reco_attr: Space_Used (45)
784        //                 reco_attr: Time_Access (47)
785        //                 reco_attr: Time_Metadata (52)
786        //                 reco_attr: Time_Modify (53)
787        //                 reco_attr: Mounted_on_FileId (55)
788        //     [Main Opcode: ACCESS (3)]
789
790        const RAW: [u8; 156] = hex!(
791            "80000098265ec1060000000000000002000186a300000004000000010000000100
792			0000180000000000000000000000000000000000000001000000000000000000000
793			0000000000c6163636573732020202020200000000000000003000000160000001f
794			4300004d1a436f6c452240ea4c70a1b52d7f97418e6601a10e02009cf2d59c00000
795			000030000003f00000009000000021010011a00b0a23a"
796        );
797
798        let msg = RpcMessage::try_from(RAW.as_ref()).expect("failed to parse message");
799        assert_eq!(msg.xid(), 643744006);
800        assert_eq!(msg.serialised_len(), 156);
801
802        let body = msg.call_body().expect("not a call rpc");
803        assert_eq!(body.rpc_version(), 2);
804        assert_eq!(body.program(), 100003);
805        assert_eq!(body.program_version(), 4);
806        assert_eq!(body.procedure(), 1);
807
808        assert_eq!(body.auth_credentials().serialised_len(), 32);
809        let params = match *body.auth_credentials() {
810            AuthFlavor::AuthUnix(ref v) => v,
811            ref v => panic!("unexpected auth type {:?}", v),
812        };
813
814        assert_eq!(params.stamp(), 0x00000000);
815        assert_eq!(params.machine_name_str(), "");
816        assert_eq!(params.uid(), 0);
817        assert_eq!(params.gid(), 0);
818        assert_eq!(params.serialised_len(), 24);
819        assert_eq!(params.gids(), Some([0].as_slice()));
820
821        assert_eq!(*body.auth_verifier(), AuthFlavor::AuthNone(None));
822        assert_eq!(body.auth_verifier().serialised_len(), 8);
823
824        assert_eq!(body.payload().len(), 88);
825
826        let serialised = msg.serialise().expect("failed to serialise");
827        assert_eq!(serialised.as_slice(), RAW.as_ref());
828    }
829
830    #[test]
831    fn test_rpcmessage_reply<'a>() {
832        // Remote Procedure Call, Type:Reply XID:0x265ec0fd
833        //     Fragment header: Last fragment, 72 bytes
834        //         1... .... .... .... .... .... .... .... = Last Fragment: Yes
835        //         .000 0000 0000 0000 0000 0000 0100 1000 = Fragment Length: 72
836        //     XID: 0x265ec0fd (643743997)
837        //     Message Type: Reply (1)
838        //     [Program: NFS (100003)]
839        //     [Program Version: 4]
840        //     [Procedure: COMPOUND (1)]
841        //     Reply State: accepted (0)
842        //     [This is a reply to a request in frame 3]
843        //     [Time from request: 0.000159000 seconds]
844        //     Verifier
845        //         Flavor: AUTH_NULL (0)
846        //         Length: 0
847        //     Accept State: RPC executed successfully (0)
848
849        const RAW: [u8; 76] = hex!(
850            "80000048265ec0fd00000001000000000000000000000000000000000000000000
851            00000c736574636c696420202020200000000100000023000000005ed2672e00000
852            0020200000000000000"
853        );
854
855        let msg = RpcMessage::try_from(RAW.as_ref()).expect("failed to parse message");
856        assert_eq!(msg.xid(), 643743997);
857        assert_eq!(msg.serialised_len(), 76);
858
859        let body = match msg.reply_body().expect("not a call rpc") {
860            ReplyBody::Accepted(b) => b,
861            _ => panic!("wrong reply type"),
862        };
863        assert_eq!(body.serialised_len(), 60);
864
865        match body.status() {
866            AcceptedStatus::Success(data) => {
867                assert_eq!(data.len(), 48);
868            }
869            _ => panic!("wrong reply status type"),
870        };
871
872        match body.auth_verifier() {
873            AuthFlavor::AuthNone(None) => {}
874            _ => panic!("wrong verifier type"),
875        };
876
877        let buf = msg.serialise().expect("failed to serialise");
878        assert_eq!(buf.as_slice(), RAW.as_ref());
879    }
880
881    #[test]
882    #[cfg(feature = "bytes")]
883    fn test_rpcmessage_reply_bytes<'a>() {
884        // Remote Procedure Call, Type:Reply XID:0x265ec0fd
885        //     Fragment header: Last fragment, 72 bytes
886        //         1... .... .... .... .... .... .... .... = Last Fragment: Yes
887        //         .000 0000 0000 0000 0000 0000 0100 1000 = Fragment Length: 72
888        //     XID: 0x265ec0fd (643743997)
889        //     Message Type: Reply (1)
890        //     [Program: NFS (100003)]
891        //     [Program Version: 4]
892        //     [Procedure: COMPOUND (1)]
893        //     Reply State: accepted (0)
894        //     [This is a reply to a request in frame 3]
895        //     [Time from request: 0.000159000 seconds]
896        //     Verifier
897        //         Flavor: AUTH_NULL (0)
898        //         Length: 0
899        //     Accept State: RPC executed successfully (0)
900
901        const RAW: [u8; 76] = hex!(
902            "80000048265ec0fd00000001000000000000000000000000000000000000000000
903            00000c736574636c696420202020200000000100000023000000005ed2672e00000
904            0020200000000000000"
905        );
906
907        let static_raw: &'static [u8] = Box::leak(Box::new(RAW));
908
909        let msg = RpcMessage::try_from(Bytes::from(static_raw)).expect("failed to parse message");
910        assert_eq!(msg.xid(), 643743997);
911        assert_eq!(msg.serialised_len(), 76);
912
913        let body = match msg.reply_body().expect("not a call rpc") {
914            ReplyBody::Accepted(b) => b,
915            _ => panic!("wrong reply type"),
916        };
917        assert_eq!(body.serialised_len(), 60);
918
919        match body.status() {
920            AcceptedStatus::Success(data) => {
921                assert_eq!(data.len(), 48);
922            }
923            _ => panic!("wrong reply status type"),
924        };
925
926        match body.auth_verifier() {
927            AuthFlavor::AuthNone(None) => {}
928            _ => panic!("wrong verifier type"),
929        };
930
931        let buf = msg.serialise().expect("failed to serialise");
932        assert_eq!(buf.as_slice(), RAW.as_ref());
933    }
934
935    #[test]
936    fn test_fuzz_message_too_long_for_type<'a>() {
937        const RAW: [u8; 39] = hex!(
938            "800000232323232300000001000000000000000000000000000000010302
939            232323232300232300"
940        );
941
942        let msg = RpcMessage::try_from(RAW.as_ref());
943        match msg {
944            Err(Error::IncompleteMessage {
945                buffer_len: b,
946                expected: e,
947            }) => {
948                assert_eq!(b, 39);
949                assert_eq!(e, 28);
950            }
951            v => panic!("expected incomplete error, got {:?}", v),
952        }
953    }
954
955    #[test]
956    #[cfg(feature = "bytes")]
957    fn test_fuzz_message_too_long_for_type_bytes<'a>() {
958        const RAW: [u8; 39] = hex!(
959            "800000232323232300000001000000000000000000000000000000010302
960            232323232300232300"
961        );
962
963        let msg = RpcMessage::try_from(Bytes::copy_from_slice(RAW.as_ref()));
964        match msg {
965            Err(Error::IncompleteMessage {
966                buffer_len: b,
967                expected: e,
968            }) => {
969                assert_eq!(b, 39);
970                assert_eq!(e, 28);
971            }
972            v => panic!("expected incomplete error, got {:?}", v),
973        }
974    }
975
976    #[test]
977    fn test_ioslice_payload() {
978        use std::io::IoSlice;
979
980        let buf1 = [1; 8];
981        let ioslice = IoSlice::new(&buf1);
982
983        let body = CallBody::<Opaque<&[u8]>, _>::new(
984            1,
985            2,
986            3,
987            AuthFlavor::AuthNone(None),
988            AuthFlavor::AuthNone(None),
989            ioslice.as_ref(),
990        );
991
992        let msg = RpcMessage::new(42, MessageType::Call(body));
993
994        assert_eq!(msg.call_body().unwrap().payload(), &buf1);
995    }
996
997    const OPAQUE_BYTE_SIZE: RangeInclusive<usize> = 0..=1025;
998
999    /// Generate a strategy that yields arbitrary byte arrays with a random
1000    /// length within the [`OPAQUE_BYTE_SIZE`] range.
1001    fn arbitrary_bytes(r: impl Into<SizeRange>) -> impl Strategy<Value = Vec<u8>> {
1002        prop::collection::vec(prop::num::u8::ANY, r)
1003    }
1004
1005    prop_compose! {
1006        fn arbitrary_unix_auth_params()(
1007            stamp in any::<u32>(),
1008            machine_name in prop::collection::vec(prop::num::u8::ANY, 0..=16),
1009            uid in any::<u32>(),
1010            gid in any::<u32>(),
1011            gids in prop::collection::vec(any::<u32>(), 0..=16),
1012        ) -> AuthUnixParams<Vec<u8>> {
1013            AuthUnixParams::new(
1014                stamp,
1015                machine_name,
1016                uid,
1017                gid,
1018                gids,
1019            )
1020        }
1021    }
1022
1023    fn arbitrary_auth_flavor() -> impl Strategy<Value = AuthFlavor<Vec<u8>>> {
1024        prop_oneof![
1025            // AuthNone
1026            of(arbitrary_bytes(0..=200))
1027                .prop_map(|opt_data| AuthFlavor::AuthNone(opt_data.map(|data| data))),
1028            // AuthUnix
1029            arbitrary_unix_auth_params().prop_map(AuthFlavor::AuthUnix),
1030            // AuthShort
1031            arbitrary_bytes(0..=200).prop_map(|data| AuthFlavor::AuthShort(data)),
1032            // Unknown
1033            (any::<u32>(), arbitrary_bytes(0..=200))
1034                .prop_map(|(id, data)| AuthFlavor::Unknown { id, data: data })
1035        ]
1036    }
1037
1038    prop_compose! {
1039        fn arbitrary_call_body()(
1040            program in any::<u32>(),
1041            program_version in any::<u32>(),
1042            procedure in any::<u32>(),
1043            auth_credentials in arbitrary_auth_flavor(),
1044            auth_verifier in arbitrary_auth_flavor(),
1045            payload in arbitrary_bytes(OPAQUE_BYTE_SIZE),
1046        ) -> CallBody<Vec<u8>, Vec<u8>> {
1047            CallBody::new(
1048                program,
1049                program_version,
1050                procedure,
1051                auth_credentials,
1052                auth_verifier,
1053                payload,
1054            )
1055        }
1056    }
1057
1058    fn arbitrary_accepted_status() -> impl Strategy<Value = AcceptedStatus<Vec<u8>>> {
1059        prop_oneof![
1060            arbitrary_bytes(OPAQUE_BYTE_SIZE).prop_map(AcceptedStatus::Success),
1061            Just(AcceptedStatus::ProgramUnavailable),
1062            (any::<u32>(), any::<u32>())
1063                .prop_map(|(low, high)| AcceptedStatus::ProgramMismatch { low, high }),
1064            Just(AcceptedStatus::ProcedureUnavailable),
1065            Just(AcceptedStatus::GarbageArgs),
1066            Just(AcceptedStatus::SystemError),
1067        ]
1068    }
1069
1070    prop_compose! {
1071        fn arbitrary_accepted_reply()(
1072            auth_verifier in arbitrary_auth_flavor(),
1073            status in arbitrary_accepted_status(),
1074        ) -> AcceptedReply<Vec<u8>, Vec<u8>> {
1075            AcceptedReply::new(
1076                auth_verifier,
1077                status
1078            )
1079        }
1080    }
1081
1082    fn arbitrary_auth_error() -> impl Strategy<Value = AuthError> {
1083        prop_oneof![
1084            Just(AuthError::Success),
1085            Just(AuthError::BadCredentials),
1086            Just(AuthError::RejectedCredentials),
1087            Just(AuthError::BadVerifier),
1088            Just(AuthError::RejectedVerifier),
1089            Just(AuthError::TooWeak),
1090            Just(AuthError::InvalidResponseVerifier),
1091            Just(AuthError::Failed),
1092        ]
1093    }
1094
1095    fn arbitrary_rejected_reply() -> impl Strategy<Value = RejectedReply> {
1096        prop_oneof![
1097            (any::<u32>(), any::<u32>())
1098                .prop_map(|(low, high)| RejectedReply::RpcVersionMismatch { low, high }),
1099            arbitrary_auth_error().prop_map(RejectedReply::AuthError),
1100        ]
1101    }
1102
1103    fn arbitrary_reply_body() -> impl Strategy<Value = ReplyBody<Vec<u8>, Vec<u8>>> {
1104        prop_oneof![
1105            arbitrary_accepted_reply().prop_map(ReplyBody::Accepted),
1106            arbitrary_rejected_reply().prop_map(ReplyBody::Denied),
1107        ]
1108    }
1109
1110    fn arbitrary_message_type() -> impl Strategy<Value = MessageType<Vec<u8>, Vec<u8>>> {
1111        prop_oneof![
1112            arbitrary_call_body().prop_map(MessageType::Call),
1113            arbitrary_reply_body().prop_map(MessageType::Reply),
1114        ]
1115    }
1116
1117    prop_compose! {
1118        fn arbitrary_rpc_message()(
1119            xid in any::<u32>(),
1120            message_type in arbitrary_message_type(),
1121        ) -> RpcMessage<Vec<u8>, Vec<u8>> {
1122            RpcMessage::new(xid, message_type)
1123        }
1124    }
1125
1126    proptest! {
1127        #[test]
1128        fn prop_round_trip(
1129            msg in arbitrary_rpc_message(),
1130        ) {
1131            let mut buf = Vec::new();
1132            msg.serialise_into(&mut buf).expect("valid message should serialise");
1133
1134            // Invariant: serialise_into() and serialise() are identical.
1135            assert_eq!(buf, msg.serialise().unwrap());
1136
1137            // Invariant: serialised_len() reports an accurate byte length.
1138            assert_eq!(msg.serialised_len() as usize, buf.len());
1139
1140            // Invariant: the serialised message length prefix is accurate.
1141            let want = expected_message_len(&buf).expect("read message header length");
1142            assert_eq!(want as usize, buf.len());
1143
1144            // Invariant: the serialised form of a valid message can always be
1145            // read back as the original message (round trip-able).
1146            let got = RpcMessage::try_from(&*buf).expect("read valid message");
1147
1148            // The deserialised message uses a different buffer type (a borrowed
1149            // slice instead of an owned vector), so they're not (currently)
1150            // directly comparable. However if the serialised representation of
1151            // the deserialised message is identical, then the messages are
1152            // identical.
1153            assert_eq!(buf, got.serialise().unwrap());
1154        }
1155    }
1156}