onc_rpc/reply/
rejected_reply.rs

1use std::{
2    convert::TryFrom,
3    io::{Cursor, Write},
4};
5
6use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
7
8use crate::Error;
9
10const REJECTED_RPC_MISMATCH: u32 = 0;
11const REJECTED_AUTH_ERROR: u32 = 1;
12
13const AUTH_ERROR_SUCCESS: u32 = 0;
14const AUTH_ERROR_BADCRED: u32 = 1;
15const AUTH_ERROR_REJECTEDCRED: u32 = 2;
16const AUTH_ERROR_BADVERF: u32 = 3;
17const AUTH_ERROR_REJECTEDVERF: u32 = 4;
18const AUTH_ERROR_TOOWEAK: u32 = 5;
19const AUTH_ERROR_INVALIDRESP: u32 = 6;
20const AUTH_ERROR_FAILED: u32 = 7;
21
22/// The response type for a rejected RPC invocation.
23#[derive(Debug, PartialEq)]
24pub enum RejectedReply {
25    /// The RPC version was not serviceable.
26    ///
27    /// Only RPC version 2 is supported.
28    RpcVersionMismatch {
29        /// The lowest supported version.
30        low: u32,
31        /// The highest supported version.
32        high: u32,
33    },
34
35    /// The authentication credentials included in the request (if any) were
36    /// rejected.
37    AuthError(AuthError),
38}
39
40impl RejectedReply {
41    /// Constructs a new `RejectedReply` by parsing the wire format read from
42    /// `r`.
43    ///
44    /// `from_cursor` advances the position of `r` to the end of the
45    /// `RejectedReply` structure.
46    pub(crate) fn from_cursor(r: &mut Cursor<&[u8]>) -> Result<Self, Error> {
47        let reply = match r.read_u32::<BigEndian>()? {
48            REJECTED_RPC_MISMATCH => Self::RpcVersionMismatch {
49                low: r.read_u32::<BigEndian>()?,
50                high: r.read_u32::<BigEndian>()?,
51            },
52            REJECTED_AUTH_ERROR => Self::AuthError(AuthError::from_cursor(r)?),
53            v => return Err(Error::InvalidRejectedReplyType(v)),
54        };
55
56        Ok(reply)
57    }
58
59    /// Serialises this `RejectedReply` into `buf`, advancing the cursor
60    /// position by [`RejectedReply::serialised_len()`] bytes.
61    pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
62        match self {
63            Self::RpcVersionMismatch { low: l, high: h } => {
64                buf.write_u32::<BigEndian>(REJECTED_RPC_MISMATCH)?;
65                buf.write_u32::<BigEndian>(*l)?;
66                buf.write_u32::<BigEndian>(*h)
67            }
68            Self::AuthError(err) => {
69                buf.write_u32::<BigEndian>(REJECTED_AUTH_ERROR)?;
70                err.serialise_into(buf)
71            }
72        }
73    }
74
75    /// Returns the on-wire length of this reply body once serialised.
76    pub fn serialised_len(&self) -> u32 {
77        let mut len = 0;
78
79        // Discriminator
80        len += 4;
81
82        // Variant length
83        len += match self {
84            Self::RpcVersionMismatch {
85                low: _low,
86                high: _high,
87            } => {
88                // low, high
89                4 + 4
90            }
91            Self::AuthError(e) => e.serialised_len(),
92        };
93
94        len
95    }
96}
97
98impl TryFrom<&[u8]> for RejectedReply {
99    type Error = Error;
100
101    fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
102        let mut c = Cursor::new(v);
103        Self::from_cursor(&mut c)
104    }
105}
106
107#[cfg(feature = "bytes")]
108impl TryFrom<crate::Bytes> for RejectedReply {
109    type Error = Error;
110
111    fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
112        use crate::bytes_ext::BytesReaderExt;
113
114        let reply = match v.try_u32()? {
115            REJECTED_RPC_MISMATCH => Self::RpcVersionMismatch {
116                low: v.try_u32()?,
117                high: v.try_u32()?,
118            },
119            REJECTED_AUTH_ERROR => Self::AuthError(AuthError::try_from(v)?),
120            v => return Err(Error::InvalidRejectedReplyType(v)),
121        };
122
123        Ok(reply)
124    }
125}
126
127/// `AuthError` describes the reason the request authentication credentials were
128/// rejected.
129#[derive(Debug, PartialEq, Clone)]
130pub enum AuthError {
131    /// This is `AUTH_OK` in the spec.
132    Success,
133
134    /// The credentials were rejected.
135    ///
136    /// This is `AUTH_BADCRED` in the spec.
137    BadCredentials,
138
139    /// The session has been invalidated.
140    ///
141    /// This typically occurs if using [`AUTH_SHORT`] and the opaque identifier
142    /// has been revoked on the server side.
143    ///
144    /// This is `AUTH_REJECTEDCRED` in the spec.
145    ///
146    /// [`AUTH_SHORT`]: crate::auth::AuthFlavor::AuthShort
147    RejectedCredentials,
148
149    /// The verifier was not acceptable.
150    ///
151    /// This is `AUTH_BADVERF` in the spec.
152    BadVerifier,
153
154    /// The verifier was rejected/expired.
155    ///
156    /// This is `AUTH_REJECTEDVERF` in the spec.
157    RejectedVerifier,
158
159    /// The authentication scheme was rejected for security reasons.
160    ///
161    /// This is `AUTH_TOOWEAK` in the spec.
162    TooWeak,
163
164    /// The response verifier is invalid.
165    ///
166    /// This is `AUTH_INVALIDRESP` in the spec.
167    InvalidResponseVerifier,
168
169    /// An unknown failure occured.
170    ///
171    /// This is `AUTH_FAILED` in the spec.
172    Failed,
173}
174
175impl AuthError {
176    pub(crate) fn from_cursor(r: &mut Cursor<&[u8]>) -> Result<Self, Error> {
177        let reply = match r.read_u32::<BigEndian>()? {
178            AUTH_ERROR_SUCCESS => Self::Success,
179            AUTH_ERROR_BADCRED => Self::BadCredentials,
180            AUTH_ERROR_REJECTEDCRED => Self::RejectedCredentials,
181            AUTH_ERROR_BADVERF => Self::BadVerifier,
182            AUTH_ERROR_REJECTEDVERF => Self::RejectedVerifier,
183            AUTH_ERROR_TOOWEAK => Self::TooWeak,
184            AUTH_ERROR_INVALIDRESP => Self::InvalidResponseVerifier,
185            AUTH_ERROR_FAILED => Self::Failed,
186            v => return Err(Error::InvalidAuthError(v)),
187        };
188
189        Ok(reply)
190    }
191
192    /// Serialises this `AuthError` into `buf`, advancing the cursor position by
193    /// [`AuthError::serialised_len()`] bytes.
194    pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
195        let id = match self {
196            Self::Success => AUTH_ERROR_SUCCESS,
197            Self::BadCredentials => AUTH_ERROR_BADCRED,
198            Self::RejectedCredentials => AUTH_ERROR_REJECTEDCRED,
199            Self::BadVerifier => AUTH_ERROR_BADVERF,
200            Self::RejectedVerifier => AUTH_ERROR_REJECTEDVERF,
201            Self::TooWeak => AUTH_ERROR_TOOWEAK,
202            Self::InvalidResponseVerifier => AUTH_ERROR_INVALIDRESP,
203            Self::Failed => AUTH_ERROR_FAILED,
204        };
205
206        buf.write_u32::<BigEndian>(id)
207    }
208
209    /// Returns the on-wire length of this reply body once serialised.
210    pub fn serialised_len(&self) -> u32 {
211        4
212    }
213}
214
215#[cfg(feature = "bytes")]
216impl TryFrom<crate::Bytes> for AuthError {
217    type Error = Error;
218
219    fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
220        use crate::bytes_ext::BytesReaderExt;
221
222        let reply = match v.try_u32()? {
223            AUTH_ERROR_SUCCESS => Self::Success,
224            AUTH_ERROR_BADCRED => Self::BadCredentials,
225            AUTH_ERROR_REJECTEDCRED => Self::RejectedCredentials,
226            AUTH_ERROR_BADVERF => Self::BadVerifier,
227            AUTH_ERROR_REJECTEDVERF => Self::RejectedVerifier,
228            AUTH_ERROR_TOOWEAK => Self::TooWeak,
229            AUTH_ERROR_INVALIDRESP => Self::InvalidResponseVerifier,
230            AUTH_ERROR_FAILED => Self::Failed,
231            v => return Err(Error::InvalidAuthError(v)),
232        };
233
234        Ok(reply)
235    }
236}