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        // NOTE: this purposely does not use an Opaque type as the opaque data
182        // is not described, is protocol specific and recommended to be of
183        // length 0.
184
185        AcceptedStatus::Success(payload)
186    }
187}
188
189impl<P> AcceptedStatus<P>
190where
191    P: AsRef<[u8]>,
192{
193    /// Serialises this `AcceptedStatus` into `buf`, advancing the cursor
194    /// position by [`AcceptedStatus::serialised_len()`] bytes.
195    pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
196        match self {
197            Self::Success(p) => {
198                buf.write_u32::<BigEndian>(REPLY_SUCCESS)?;
199                buf.write_all(p.as_ref())
200            }
201            Self::ProgramUnavailable => buf.write_u32::<BigEndian>(REPLY_PROG_UNAVAIL),
202            Self::ProgramMismatch { low: l, high: h } => {
203                buf.write_u32::<BigEndian>(REPLY_PROG_MISMATCH)?;
204                buf.write_u32::<BigEndian>(*l)?;
205                buf.write_u32::<BigEndian>(*h)
206            }
207            Self::ProcedureUnavailable => buf.write_u32::<BigEndian>(REPLY_PROC_UNAVAIL),
208            Self::GarbageArgs => buf.write_u32::<BigEndian>(REPLY_GARBAGE_ARGS),
209            Self::SystemError => buf.write_u32::<BigEndian>(REPLY_SYSTEM_ERR),
210        }
211    }
212
213    /// Returns the on-wire length of this type once serialised.
214    pub fn serialised_len(&self) -> u32 {
215        let mut len = 0;
216
217        // Discriminator
218        len += 4;
219
220        // Variant length
221        len += match self {
222            Self::Success(p) => p.as_ref().len() as u32,
223            Self::ProgramUnavailable => 0,
224            Self::ProgramMismatch { low: _l, high: _h } => 8,
225            Self::ProcedureUnavailable => 0,
226            Self::GarbageArgs => 0,
227            Self::SystemError => 0,
228        };
229
230        len
231    }
232}
233
234impl<'a> TryFrom<&'a [u8]> for AcceptedStatus<&'a [u8]> {
235    type Error = Error;
236
237    fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
238        let mut c = Cursor::new(v);
239        AcceptedStatus::from_cursor(&mut c)
240    }
241}
242
243#[cfg(feature = "bytes")]
244impl TryFrom<crate::Bytes> for AcceptedStatus<crate::Bytes> {
245    type Error = Error;
246
247    fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
248        use crate::bytes_ext::BytesReaderExt;
249
250        let reply = match v.try_u32()? {
251            REPLY_SUCCESS => Self::Success(v),
252            REPLY_PROG_UNAVAIL => Self::ProgramUnavailable,
253            REPLY_PROG_MISMATCH => Self::ProgramMismatch {
254                low: v.try_u32()?,
255                high: v.try_u32()?,
256            },
257            REPLY_PROC_UNAVAIL => Self::ProcedureUnavailable,
258            REPLY_GARBAGE_ARGS => Self::GarbageArgs,
259            REPLY_SYSTEM_ERR => Self::SystemError,
260            v => return Err(Error::InvalidReplyStatus(v)),
261        };
262
263        Ok(reply)
264    }
265}
266
267#[cfg(test)]
268mod tests {
269
270    use super::*;
271
272    // A compile-time test that ensures a payload can differ in type from the
273    // auth buffer.
274    #[test]
275    fn test_differing_payload_type() {
276        let binding = vec![42];
277        let auth = AuthFlavor::AuthNone(Some(binding.as_slice()));
278        let payload = [42, 42, 42, 42];
279
280        let _reply: AcceptedReply<&[u8], [u8; 4]> =
281            AcceptedReply::new(auth, AcceptedStatus::Success(payload));
282    }
283}