onc_rpc/reply/
reply_body.rs

1use std::{
2    convert::TryFrom,
3    io::{Cursor, Write},
4};
5
6use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
7
8use super::{AcceptedReply, RejectedReply};
9use crate::Error;
10
11const REPLY_ACCEPTED: u32 = 0;
12const REPLY_DENIED: u32 = 1;
13
14/// `ReplyBody` defines the response to an RPC invocation.
15#[derive(Debug, PartialEq)]
16pub enum ReplyBody<T, P>
17where
18    T: AsRef<[u8]>,
19    P: AsRef<[u8]>,
20{
21    /// The server accepted the request credentials.
22    Accepted(AcceptedReply<T, P>),
23
24    /// The server rejected the request credentials.
25    Denied(RejectedReply),
26}
27
28impl<'a> ReplyBody<&'a [u8], &'a [u8]> {
29    pub(crate) fn from_cursor(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
30        match r.read_u32::<BigEndian>()? {
31            REPLY_ACCEPTED => Ok(ReplyBody::Accepted(AcceptedReply::from_cursor(r)?)),
32            REPLY_DENIED => Ok(ReplyBody::Denied(RejectedReply::from_cursor(r)?)),
33            v => Err(Error::InvalidReplyType(v)),
34        }
35    }
36}
37
38impl<T, P> ReplyBody<T, P>
39where
40    T: AsRef<[u8]>,
41    P: AsRef<[u8]>,
42{
43    /// Serialises this `ReplyBody` into `buf`, advancing the cursor position by
44    /// [`ReplyBody::serialised_len()`] bytes.
45    pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
46        match self {
47            Self::Accepted(b) => {
48                buf.write_u32::<BigEndian>(REPLY_ACCEPTED)?;
49                b.serialise_into(buf)
50            }
51            Self::Denied(b) => {
52                buf.write_u32::<BigEndian>(REPLY_DENIED)?;
53                b.serialise_into(buf)
54            }
55        }
56    }
57
58    /// Returns the on-wire length of this `ReplyBody` once serialised,
59    /// including the message header.
60    pub fn serialised_len(&self) -> u32 {
61        let mut len = 0;
62
63        // Discriminator
64        len += 4;
65
66        // Variant length
67        len += match self {
68            Self::Accepted(b) => b.serialised_len(),
69            Self::Denied(b) => b.serialised_len(),
70        };
71
72        len
73    }
74}
75
76impl<'a> TryFrom<&'a [u8]> for ReplyBody<&'a [u8], &'a [u8]> {
77    type Error = Error;
78
79    fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
80        let mut c = Cursor::new(v);
81        ReplyBody::from_cursor(&mut c)
82    }
83}
84
85#[cfg(feature = "bytes")]
86impl TryFrom<crate::Bytes> for ReplyBody<crate::Bytes, crate::Bytes> {
87    type Error = Error;
88
89    fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
90        use crate::bytes_ext::BytesReaderExt;
91
92        match v.try_u32()? {
93            REPLY_ACCEPTED => Ok(Self::Accepted(AcceptedReply::try_from(v)?)),
94            REPLY_DENIED => Ok(Self::Denied(RejectedReply::try_from(v)?)),
95            v => Err(Error::InvalidReplyType(v)),
96        }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use crate::auth::AuthFlavor;
103
104    use super::*;
105
106    // A compile-time test that ensures a payload can differ in type from the
107    // auth buffer.
108    #[test]
109    fn test_differing_payload_type() {
110        let binding = vec![42];
111        let auth = AuthFlavor::AuthNone(Some(binding.as_slice()));
112        let payload = [42, 42, 42, 42];
113
114        let _reply: ReplyBody<&[u8], [u8; 4]> = ReplyBody::Accepted(AcceptedReply::new(
115            auth,
116            crate::AcceptedStatus::Success(payload),
117        ));
118    }
119}