onc_rpc/reply/
accepted_reply.rs

1use std::{
2    convert::TryFrom,
3    io::{Cursor, Write},
4};
5
6use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
7
8use crate::{auth::AuthFlavor, Error};
9
10const REPLY_SUCCESS: u32 = 0;
11const REPLY_PROG_UNAVAIL: u32 = 1;
12const REPLY_PROG_MISMATCH: u32 = 2;
13const REPLY_PROC_UNAVAIL: u32 = 3;
14const REPLY_GARBAGE_ARGS: u32 = 4;
15const REPLY_SYSTEM_ERR: u32 = 5;
16
17/// A type sent in response to a request that contains credentials accepted by
18/// the server.
19#[derive(Debug, PartialEq)]
20pub struct AcceptedReply<T, P>
21where
22    T: AsRef<[u8]>,
23    P: AsRef<[u8]>,
24{
25    auth_verifier: AuthFlavor<T>,
26    status: AcceptedStatus<P>,
27}
28
29impl<'a> AcceptedReply<&'a [u8], &'a [u8]> {
30    /// Constructs a new `AcceptedReply` by parsing the wire format read from
31    /// `r`.
32    ///
33    /// `from_cursor` advances the position of `r` to the end of the
34    /// `AcceptedReply` structure.
35    pub(crate) fn from_cursor(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
36        Ok(AcceptedReply {
37            auth_verifier: AuthFlavor::from_cursor(r)?,
38            status: AcceptedStatus::from_cursor(r)?,
39        })
40    }
41}
42
43impl<T, P> AcceptedReply<T, P>
44where
45    T: AsRef<[u8]>,
46    P: AsRef<[u8]>,
47{
48    /// Constructs a new `AcceptedReply` with the specified [`AcceptedStatus`].
49    pub fn new(auth_verifier: AuthFlavor<T>, status: AcceptedStatus<P>) -> Self {
50        Self {
51            auth_verifier,
52            status,
53        }
54    }
55
56    /// Serialises this `AcceptedReply` into `buf`, advancing the cursor
57    /// position by [`AcceptedReply::serialised_len()`] bytes.
58    pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
59        self.auth_verifier.serialise_into(&mut buf)?;
60        self.status.serialise_into(&mut buf)
61    }
62
63    /// Returns the on-wire length of this type once serialised.
64    pub fn serialised_len(&self) -> u32 {
65        self.auth_verifier.serialised_len() + self.status.serialised_len()
66    }
67
68    /// Returns the auth verifier for use by the client to validate the server.
69    pub fn auth_verifier(&self) -> &AuthFlavor<T> {
70        &self.auth_verifier
71    }
72
73    /// Returns the status code of the response.
74    pub fn status(&self) -> &AcceptedStatus<P> {
75        &self.status
76    }
77}
78
79impl<'a> TryFrom<&'a [u8]> for AcceptedReply<&'a [u8], &'a [u8]> {
80    type Error = Error;
81
82    fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
83        let mut c = Cursor::new(v);
84        AcceptedReply::from_cursor(&mut c)
85    }
86}
87
88#[cfg(feature = "bytes")]
89impl TryFrom<crate::Bytes> for AcceptedReply<crate::Bytes, crate::Bytes> {
90    type Error = Error;
91
92    fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
93        use crate::Buf;
94
95        // Deserialise the auth flavor using a copy of v, and then advance the
96        // pointer in v.
97        let auth_verifier = AuthFlavor::try_from(v.clone())?;
98        v.advance(auth_verifier.serialised_len() as usize);
99
100        Ok(Self {
101            auth_verifier,
102            status: AcceptedStatus::try_from(v)?,
103        })
104    }
105}
106
107/// The response status code for a request that contains valid credentials.
108#[derive(Debug, PartialEq, Clone)]
109pub enum AcceptedStatus<P>
110where
111    P: AsRef<[u8]>,
112{
113    /// The RPC was successful, and the response payload is contained in the
114    /// variant.
115    Success(P),
116
117    /// The specified program identifier has no handler in this server.
118    ///
119    /// This is `PROG_UNAVAIL` in the spec.
120    ProgramUnavailable,
121
122    /// The program to invoke was found, but it doesn't support the requested
123    /// version.
124    ///
125    /// This is `PROG_MISMATCH` in the spec.
126    ProgramMismatch {
127        /// The lowest supported program version.
128        low: u32,
129
130        /// The highest supported program version.
131        high: u32,
132    },
133
134    /// The program to invoke was found, but the procedure number is not
135    /// recognised.
136    ///
137    /// This is `PROC_UNAVAIL` in the spec.
138    ProcedureUnavailable,
139
140    /// The arguments provided to the RPC endpoint were not serialised
141    /// correctly, or otherwise unacceptable.
142    ///
143    /// This is `GARBAGE_ARGS` in the spec.
144    GarbageArgs,
145
146    /// The server experienced an internal error.
147    ///
148    /// This is `SYSTEM_ERR` in the spec.
149    SystemError,
150}
151
152impl<'a> AcceptedStatus<&'a [u8]> {
153    /// Constructs a new `AcceptedStatus` by parsing the wire format read from
154    /// `r`.
155    ///
156    /// `from_cursor` advances the position of `r` to the end of the
157    /// `AcceptedStatus` structure.
158    pub(crate) fn from_cursor(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
159        // Read the RPC version and stop if it is not 2.
160        let reply = match r.read_u32::<BigEndian>()? {
161            REPLY_SUCCESS => AcceptedStatus::new_success(r),
162            REPLY_PROG_UNAVAIL => AcceptedStatus::ProgramUnavailable,
163            REPLY_PROG_MISMATCH => AcceptedStatus::ProgramMismatch {
164                low: r.read_u32::<BigEndian>()?,
165                high: r.read_u32::<BigEndian>()?,
166            },
167            REPLY_PROC_UNAVAIL => AcceptedStatus::ProcedureUnavailable,
168            REPLY_GARBAGE_ARGS => AcceptedStatus::GarbageArgs,
169            REPLY_SYSTEM_ERR => AcceptedStatus::SystemError,
170            v => return Err(Error::InvalidReplyStatus(v)),
171        };
172
173        Ok(reply)
174    }
175
176    fn new_success(r: &mut Cursor<&'a [u8]>) -> Self {
177        let data = *r.get_ref();
178        let start = r.position() as usize;
179        let payload = &data[start..];
180
181        AcceptedStatus::Success(payload)
182    }
183}
184
185impl<P> AcceptedStatus<P>
186where
187    P: AsRef<[u8]>,
188{
189    /// Serialises this `AcceptedStatus` into `buf`, advancing the cursor
190    /// position by [`AcceptedStatus::serialised_len()`] bytes.
191    pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
192        match self {
193            Self::Success(p) => {
194                buf.write_u32::<BigEndian>(REPLY_SUCCESS)?;
195                buf.write_all(p.as_ref())
196            }
197            Self::ProgramUnavailable => buf.write_u32::<BigEndian>(REPLY_PROG_UNAVAIL),
198            Self::ProgramMismatch { low: l, high: h } => {
199                buf.write_u32::<BigEndian>(REPLY_PROG_MISMATCH)?;
200                buf.write_u32::<BigEndian>(*l)?;
201                buf.write_u32::<BigEndian>(*h)
202            }
203            Self::ProcedureUnavailable => buf.write_u32::<BigEndian>(REPLY_PROC_UNAVAIL),
204            Self::GarbageArgs => buf.write_u32::<BigEndian>(REPLY_GARBAGE_ARGS),
205            Self::SystemError => buf.write_u32::<BigEndian>(REPLY_SYSTEM_ERR),
206        }
207    }
208
209    /// Returns the on-wire length of this type once serialised.
210    pub fn serialised_len(&self) -> u32 {
211        let mut len = 0;
212
213        // Discriminator
214        len += 4;
215
216        // Variant length
217        len += match self {
218            Self::Success(p) => p.as_ref().len() as u32,
219            Self::ProgramUnavailable => 0,
220            Self::ProgramMismatch { low: _l, high: _h } => 8,
221            Self::ProcedureUnavailable => 0,
222            Self::GarbageArgs => 0,
223            Self::SystemError => 0,
224        };
225
226        len
227    }
228}
229
230impl<'a> TryFrom<&'a [u8]> for AcceptedStatus<&'a [u8]> {
231    type Error = Error;
232
233    fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
234        let mut c = Cursor::new(v);
235        AcceptedStatus::from_cursor(&mut c)
236    }
237}
238
239#[cfg(feature = "bytes")]
240impl TryFrom<crate::Bytes> for AcceptedStatus<crate::Bytes> {
241    type Error = Error;
242
243    fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
244        use crate::bytes_ext::BytesReaderExt;
245
246        let reply = match v.try_u32()? {
247            REPLY_SUCCESS => Self::Success(v),
248            REPLY_PROG_UNAVAIL => Self::ProgramUnavailable,
249            REPLY_PROG_MISMATCH => Self::ProgramMismatch {
250                low: v.try_u32()?,
251                high: v.try_u32()?,
252            },
253            REPLY_PROC_UNAVAIL => Self::ProcedureUnavailable,
254            REPLY_GARBAGE_ARGS => Self::GarbageArgs,
255            REPLY_SYSTEM_ERR => Self::SystemError,
256            v => return Err(Error::InvalidReplyStatus(v)),
257        };
258
259        Ok(reply)
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266
267    // A compile-time test that ensures a payload can differ in type from the
268    // auth buffer.
269    #[test]
270    fn test_differing_payload_type() {
271        let auth = AuthFlavor::AuthNone(Some(vec![42]));
272        let payload = [42, 42, 42, 42];
273
274        let _reply: AcceptedReply<Vec<u8>, [u8; 4]> =
275            AcceptedReply::new(auth, AcceptedStatus::Success(payload));
276    }
277}