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}