faster_stun/attribute/error.rs
1use crate::{util, StunError};
2
3use bytes::{BufMut, BytesMut};
4use num_enum::TryFromPrimitive;
5
6use std::cmp::{Eq, PartialEq};
7use std::convert::{Into, TryFrom};
8
9/// The following error codes, along with their recommended reason
10/// phrases, are defined:
11///
12/// 300 Try Alternate: The client should contact an alternate server for
13/// this request. This error response MUST only be sent if the
14/// request included either a USERNAME or USERHASH attribute and a
15/// valid MESSAGE-INTEGRITY or MESSAGE-INTEGRITY-SHA256 attribute;
16/// otherwise, it MUST NOT be sent and error code 400 (Bad Request)
17/// is suggested. This error response MUST be protected with the
18/// MESSAGE-INTEGRITY or MESSAGE-INTEGRITY-SHA256 attribute, and
19/// receivers MUST validate the MESSAGE-INTEGRITY or MESSAGE-
20/// INTEGRITY-SHA256 of this response before redirecting themselves
21/// to an alternate server.
22/// Note: Failure to generate and validate message integrity for a
23/// 300 response allows an on-path attacker to falsify a 300
24/// response thus causing subsequent STUN messages to be sent to a
25/// victim.
26///
27/// 400 Bad Request: The request was malformed. The client SHOULD NOT
28/// retry the request without modification from the previous
29/// attempt. The server may not be able to generate a valid
30/// MESSAGE-INTEGRITY or MESSAGE-INTEGRITY-SHA256 for this error, so
31/// the client MUST NOT expect a valid MESSAGE-INTEGRITY or MESSAGE-
32/// INTEGRITY-SHA256 attribute on this response.
33///
34/// 401 Unauthenticated: The request did not contain the correct
35/// credentials to proceed. The client should retry the request
36/// with proper credentials.
37///
38/// 420 Unknown Attribute: The server received a STUN packet containing
39/// a comprehension-required attribute that it did not understand.
40/// The server MUST put this unknown attribute in the UNKNOWN-
41/// ATTRIBUTE attribute of its error response.
42///
43/// 438 Stale Nonce: The NONCE used by the client was no longer valid.
44/// The client should retry, using the NONCE provided in the
45/// response.
46///
47/// 500 Server Error: The server has suffered a temporary error. The
48/// client should try again.
49#[repr(u16)]
50#[derive(TryFromPrimitive, PartialEq, Eq, Copy, Clone, Debug)]
51pub enum Kind {
52 TryAlternate = 0x0300,
53 BadRequest = 0x0400,
54 Unauthorized = 0x0401,
55 Forbidden = 0x0403,
56 RequestTimedout = 0x0408,
57 UnknownAttribute = 0x0414,
58 AllocationMismatch = 0x0425,
59 StaleNonce = 0x0426,
60 AddressFamilyNotSupported = 0x0428,
61 WrongCredentials = 0x0429,
62 UnsupportedTransportAddress = 0x042A,
63 AllocationQuotaReached = 0x0456,
64 ServerError = 0x0500,
65 InsufficientCapacity = 0x0508,
66}
67
68/// [RFC3629]: https://datatracker.ietf.org/doc/html/rfc3629
69/// [RFC7231]: https://datatracker.ietf.org/doc/html/rfc7231
70/// [RFC3261]: https://datatracker.ietf.org/doc/html/rfc3261
71/// [RFC3629]: https://datatracker.ietf.org/doc/html/rfc3629
72///
73/// The ERROR-CODE attribute is used in error response messages. It
74/// contains a numeric error code value in the range of 300 to 699 plus a
75/// textual reason phrase encoded in UTF-8 [RFC3629]; it is also
76/// consistent in its code assignments and semantics with SIP [RFC3261]
77/// and HTTP [RFC7231]. The reason phrase is meant for diagnostic
78/// purposes and can be anything appropriate for the error code.
79/// Recommended reason phrases for the defined error codes are included
80/// in the IANA registry for error codes. The reason phrase MUST be a
81/// UTF-8-encoded [RFC3629] sequence of fewer than 128 characters (which
82/// can be as long as 509 bytes when encoding them or 763 bytes when
83/// decoding them).
84///
85/// ```text
86/// 0 1 2 3
87/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
88/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
89/// | Reserved, should be 0 |Class| Number |
90/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
91/// | Reason Phrase (variable) ..
92/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
93///
94/// Figure 7: Format of ERROR-CODE Attribute
95/// ```
96///
97/// To facilitate processing, the class of the error code (the hundreds
98/// digit) is encoded separately from the rest of the code, as shown in
99/// Figure 7.
100///
101/// The Reserved bits SHOULD be 0 and are for alignment on 32-bit
102/// boundaries. Receivers MUST ignore these bits. The Class represents
103/// the hundreds digit of the error code. The value MUST be between 3
104/// and 6. The Number represents the binary encoding of the error code
105/// modulo 100, and its value MUST be between 0 and 99.
106#[derive(Clone, Debug)]
107pub struct Error<'a> {
108 pub code: u16,
109 pub message: &'a str,
110}
111
112impl Error<'_> {
113 /// create error from error type.
114 ///
115 /// # Example
116 ///
117 /// ```no_run
118 /// use faster_stun::attribute::*;
119 ///
120 /// Error::from(ErrKind::TryAlternate);
121 /// ```
122 pub fn from(code: Kind) -> Self {
123 Self {
124 code: code as u16,
125 message: code.into(),
126 }
127 }
128
129 /// encode the error type as bytes.
130 ///
131 /// # Unit Test
132 ///
133 /// ```
134 /// use bytes::BytesMut;
135 /// use faster_stun::attribute::*;
136 ///
137 /// let buffer = [
138 /// 0x00u8, 0x00, 0x03, 0x00, 0x54, 0x72, 0x79, 0x20, 0x41, 0x6c, 0x74,
139 /// 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65,
140 /// ];
141 ///
142 /// let mut buf = BytesMut::with_capacity(1280);
143 /// let error = Error::from(ErrKind::TryAlternate);
144 /// error.into(&mut buf);
145 /// assert_eq!(&buf[..], &buffer);
146 /// ```
147 pub fn into(self, buf: &mut BytesMut) {
148 buf.put_u16(0x0000);
149 buf.put_u16(self.code);
150 buf.put(self.message.as_bytes());
151 }
152}
153
154impl<'a> TryFrom<&'a [u8]> for Error<'a> {
155 type Error = StunError;
156
157 /// # Unit Test
158 ///
159 /// ```
160 /// use faster_stun::attribute::*;
161 /// use std::convert::TryFrom;
162 ///
163 /// let buffer = [
164 /// 0x00u8, 0x00, 0x03, 0x00, 0x54, 0x72, 0x79, 0x20, 0x41, 0x6c, 0x74,
165 /// 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65,
166 /// ];
167 ///
168 /// let error = Error::try_from(&buffer[..]).unwrap();
169 /// assert_eq!(error.code, ErrKind::TryAlternate as u16);
170 /// assert_eq!(error.message, "Try Alternate");
171 /// ```
172 fn try_from(packet: &'a [u8]) -> Result<Self, Self::Error> {
173 if !(packet.len() >= 4) {
174 return Err(StunError::InvalidInput);
175 }
176
177 if !(util::as_u16(&packet[..2]) == 0x0000) {
178 return Err(StunError::InvalidInput);
179 }
180
181 Ok(Self {
182 code: util::as_u16(&packet[2..4]),
183 message: std::str::from_utf8(&packet[4..]).map_err(|_| StunError::FatalError)?,
184 })
185 }
186}
187
188impl From<Kind> for &'static str {
189 /// # Unit Test
190 ///
191 /// ```
192 /// use faster_stun::attribute::*;
193 /// use std::convert::Into;
194 ///
195 /// let err: &'static str = ErrKind::TryAlternate.into();
196 /// assert_eq!(err, "Try Alternate");
197 /// ```
198 #[rustfmt::skip]
199 fn from(val: Kind) -> Self {
200 match val {
201 Kind::TryAlternate => "Try Alternate",
202 Kind::BadRequest => "Bad Request",
203 Kind::Unauthorized => "Unauthorized",
204 Kind::Forbidden => "Forbidden",
205 Kind::RequestTimedout => "Request Timed out",
206 Kind::UnknownAttribute => "Unknown Attribute",
207 Kind::AllocationMismatch => "Allocation Mismatch",
208 Kind::StaleNonce => "Stale Nonce",
209 Kind::AddressFamilyNotSupported => "Address Family not Supported",
210 Kind::WrongCredentials => "Wrong Credentials",
211 Kind::UnsupportedTransportAddress => "Unsupported Transport Address",
212 Kind::AllocationQuotaReached => "Allocation Quota Reached",
213 Kind::ServerError => "Server Error",
214 Kind::InsufficientCapacity => "Insufficient Capacity",
215 }
216 }
217}
218
219impl Eq for Error<'_> {}
220impl PartialEq for Error<'_> {
221 fn eq(&self, other: &Self) -> bool {
222 self.code == other.code
223 }
224}