stun_types/attribute/
error.rs

1// Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::convert::TryFrom;
10
11use byteorder::{BigEndian, ByteOrder};
12
13use crate::message::{StunParseError, StunWriteError};
14
15use super::{
16    Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
17    AttributeWriteExt, RawAttribute,
18};
19
20/// The ErrorCode [`Attribute`]
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ErrorCode {
23    code: u16,
24    reason: String,
25}
26impl AttributeStaticType for ErrorCode {
27    const TYPE: AttributeType = AttributeType(0x0009);
28}
29impl Attribute for ErrorCode {
30    fn get_type(&self) -> AttributeType {
31        Self::TYPE
32    }
33
34    fn length(&self) -> u16 {
35        self.reason.len() as u16 + 4
36    }
37}
38
39impl AttributeWrite for ErrorCode {
40    fn to_raw(&self) -> RawAttribute {
41        let mut data = Vec::with_capacity(self.length() as usize);
42        data.push(0u8);
43        data.push(0u8);
44        data.push((self.code / 100) as u8);
45        data.push((self.code % 100) as u8);
46        data.extend(self.reason.as_bytes());
47        RawAttribute::new_owned(ErrorCode::TYPE, data.into_boxed_slice())
48    }
49
50    fn write_into_unchecked(&self, dest: &mut [u8]) {
51        let len = self.padded_len();
52        let mut offset = self.write_header_unchecked(dest);
53        offset += self.write_into_data(&mut dest[offset..]);
54        if len - offset > 0 {
55            dest[offset..len].fill(0);
56        }
57    }
58}
59
60impl AttributeFromRaw<'_> for ErrorCode {
61    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
62    where
63        Self: Sized,
64    {
65        Self::try_from(raw)
66    }
67}
68
69impl TryFrom<&RawAttribute<'_>> for ErrorCode {
70    type Error = StunParseError;
71
72    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
73        raw.check_type_and_len(Self::TYPE, 4..=763 + 4)?;
74        let code_h = (raw.value[2] & 0x7) as u16;
75        let code_tens = raw.value[3] as u16;
76        if !(3..7).contains(&code_h) || code_tens > 99 {
77            return Err(StunParseError::InvalidAttributeData);
78        }
79        let code = code_h * 100 + code_tens;
80        Ok(Self {
81            code,
82            reason: std::str::from_utf8(&raw.value[4..])
83                .map_err(|_| StunParseError::InvalidAttributeData)?
84                .to_owned(),
85        })
86    }
87}
88
89/// Builder for an [`ErrorCode`]
90#[derive(Debug)]
91pub struct ErrorCodeBuilder<'reason> {
92    code: u16,
93    reason: Option<&'reason str>,
94}
95
96impl<'reason> ErrorCodeBuilder<'reason> {
97    fn new(code: u16) -> Self {
98        Self { code, reason: None }
99    }
100
101    /// Set the custom reason for this [`ErrorCode`]
102    pub fn reason(mut self, reason: &'reason str) -> Self {
103        self.reason = Some(reason);
104        self
105    }
106
107    /// Create the [`ErrorCode`] with the configured paramaters
108    ///
109    /// # Errors
110    ///
111    /// - When the code value is out of range [300, 699]
112    pub fn build(self) -> Result<ErrorCode, StunWriteError> {
113        if !(300..700).contains(&self.code) {
114            return Err(StunWriteError::OutOfRange {
115                value: self.code as usize,
116                min: 300,
117                max: 699,
118            });
119        }
120        let reason = self
121            .reason
122            .unwrap_or_else(|| ErrorCode::default_reason_for_code(self.code))
123            .to_owned();
124        Ok(ErrorCode {
125            code: self.code,
126            reason,
127        })
128    }
129}
130
131impl ErrorCode {
132    /// Try an alternate server.  The
133    /// [`AlternateServer`](crate::attribute::alternate::AlternateServer) or
134    /// [`AlternateDomain`](crate::attribute::alternate::AlternateDomain) contains the location of
135    /// where to forward this request.
136    pub const TRY_ALTERNATE: u16 = 301;
137    /// The request was malformed and could not be processed.
138    pub const BAD_REQUEST: u16 = 400;
139    /// The required credentials were not found or did not match.
140    pub const UNAUTHORIZED: u16 = 401;
141    /// Not allowed to access this resource.
142    pub const FORBIDDEN: u16 = 403;
143    /// An unknown comprehension required attribute was present.  The [`UnknownAttributes`]
144    /// contains the specific attribute/s.
145    pub const UNKNOWN_ATTRIBUTE: u16 = 420;
146    /// The allocation already exists on this server.
147    pub const ALLOCATION_MISMATCH: u16 = 437;
148    /// The nonce is no longer valid.
149    pub const STALE_NONCE: u16 = 438;
150    /// The address family (IPv4, IPv6) is not supported.
151    pub const ADDRESS_FAMILY_NOT_SUPPORTED: u16 = 440;
152    /// Incorrect credentials provided.
153    pub const WRONG_CREDENTIALS: u16 = 441;
154    /// The transport protocol (UDP, TCP) is not supported.
155    pub const UNSUPPORTED_TRANSPORT_PROTOCOL: u16 = 442;
156    /// The peer address family does not match the TURN allocation.
157    pub const PEER_ADDRESS_FAMILY_MISMATCH: u16 = 443;
158    /// This username has reached its limit of allocations currently allowed.
159    pub const ALLOCATION_QUOTA_REACHED: u16 = 486;
160    /// Requestor must switch ICE roles.
161    pub const ROLE_CONFLICT: u16 = 487;
162    /// An unspecificed error has occurred.
163    pub const SERVER_ERROR: u16 = 500;
164    /// The server does not have capacity to handle this request.
165    pub const INSUFFICIENT_CAPACITY: u16 = 508;
166
167    /// Create a builder for creating a new [`ErrorCode`] [`Attribute`]
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// # use stun_types::attribute::*;
173    /// let error = ErrorCode::builder (400).reason("bad error").build().unwrap();
174    /// assert_eq!(error.code(), 400);
175    /// assert_eq!(error.reason(), "bad error");
176    /// ```
177    pub fn builder<'reason>(code: u16) -> ErrorCodeBuilder<'reason> {
178        ErrorCodeBuilder::new(code)
179    }
180
181    /// Create a new [`ErrorCode`] [`Attribute`]
182    ///
183    /// # Errors
184    ///
185    /// - When the code value is out of range [300, 699]
186    ///
187    /// # Examples
188    ///
189    /// ```
190    /// # use stun_types::attribute::*;
191    /// let error = ErrorCode::new (400, "bad error").unwrap();
192    /// assert_eq!(error.code(), 400);
193    /// assert_eq!(error.reason(), "bad error");
194    /// ```
195    pub fn new(code: u16, reason: &str) -> Result<Self, StunWriteError> {
196        if !(300..700).contains(&code) {
197            return Err(StunWriteError::OutOfRange {
198                value: code as usize,
199                min: 300,
200                max: 699,
201            });
202        }
203        Ok(Self {
204            code,
205            reason: reason.to_owned(),
206        })
207    }
208
209    /// The error code value
210    ///
211    /// # Examples
212    ///
213    /// ```
214    /// # use stun_types::attribute::*;
215    /// let error = ErrorCode::new (400, "bad error").unwrap();
216    /// assert_eq!(error.code(), 400);
217    /// ```
218    pub fn code(&self) -> u16 {
219        self.code
220    }
221
222    /// The error code reason string
223    ///
224    /// # Examples
225    ///
226    /// ```
227    /// # use stun_types::attribute::*;
228    /// let error = ErrorCode::new (400, "bad error").unwrap();
229    /// assert_eq!(error.reason(), "bad error");
230    /// ```
231    pub fn reason(&self) -> &str {
232        &self.reason
233    }
234
235    /// Return some default reason strings for some error code values
236    ///
237    /// Currently the following are supported.
238    ///
239    ///  - 301 -> Try Alternate
240    ///  - 400 -> Bad Request
241    ///  - 401 -> Unauthorized
242    ///  - 403 -> Forbidden
243    ///  - 420 -> Unknown Attribute
244    ///  - 437 -> Allocation Mismatch
245    ///  - 438 -> Stale Nonce
246    ///  - 440 -> Address Family Not Supported
247    ///  - 441 -> Wrong Credentials
248    ///  - 442 -> Supported Transport Protocol
249    ///  - 443 -> Peer Address Family Mismatch
250    ///  - 486 -> Allocation Quota Reached
251    ///  - 487 -> Role Conflict
252    ///  - 500 -> Server Error
253    ///  - 508 -> Insufficient Capacity
254    pub fn default_reason_for_code(code: u16) -> &'static str {
255        match code {
256            Self::TRY_ALTERNATE => "Try Alternate",
257            Self::BAD_REQUEST => "Bad Request",
258            Self::UNAUTHORIZED => "Unauthorized",
259            Self::FORBIDDEN => "Forbidden",
260            Self::UNKNOWN_ATTRIBUTE => "Unknown Attribute",
261            Self::ALLOCATION_MISMATCH => "Allocation Mismatch",
262            Self::STALE_NONCE => "Stale Nonce",
263            Self::ADDRESS_FAMILY_NOT_SUPPORTED => "Address Family Not Supported",
264            Self::WRONG_CREDENTIALS => "Wrong Credentials",
265            Self::UNSUPPORTED_TRANSPORT_PROTOCOL => "Unsupported Transport Protocol",
266            Self::PEER_ADDRESS_FAMILY_MISMATCH => "Peer Address Family Mismatch",
267            Self::ALLOCATION_QUOTA_REACHED => "Allocation Quota Reached",
268            Self::ROLE_CONFLICT => "Role Conflict",
269            Self::SERVER_ERROR => "Server Error",
270            Self::INSUFFICIENT_CAPACITY => "Insufficient Capacity",
271            _ => "Unknown",
272        }
273    }
274
275    fn write_into_data(&self, dest: &mut [u8]) -> usize {
276        dest[0] = 0u8;
277        dest[1] = 0u8;
278        dest[2] = (self.code / 100) as u8;
279        dest[3] = (self.code % 100) as u8;
280        let bytes = self.reason.as_bytes();
281        dest[4..4 + bytes.len()].copy_from_slice(bytes);
282        4 + bytes.len()
283    }
284}
285
286impl std::fmt::Display for ErrorCode {
287    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288        write!(f, "{}: {} '{}'", Self::TYPE, self.code, self.reason)
289    }
290}
291
292/// The UnknownAttributes [`Attribute`]
293#[derive(Debug, Clone, PartialEq, Eq)]
294pub struct UnknownAttributes {
295    attributes: Vec<AttributeType>,
296}
297impl AttributeStaticType for UnknownAttributes {
298    const TYPE: AttributeType = AttributeType(0x000A);
299}
300impl Attribute for UnknownAttributes {
301    fn get_type(&self) -> AttributeType {
302        Self::TYPE
303    }
304    fn length(&self) -> u16 {
305        (self.attributes.len() as u16) * 2
306    }
307}
308impl AttributeWrite for UnknownAttributes {
309    fn to_raw(&self) -> RawAttribute {
310        let mut data = vec![0; self.length() as usize];
311        self.write_into_data(&mut data);
312        RawAttribute::new_owned(UnknownAttributes::TYPE, data.into_boxed_slice())
313    }
314
315    fn write_into_unchecked(&self, dest: &mut [u8]) {
316        let len = self.padded_len();
317        let mut offset = self.write_header_unchecked(dest);
318        offset += self.write_into_data(&mut dest[offset..]);
319        if len - offset > 0 {
320            dest[offset..len].fill(0);
321        }
322    }
323}
324
325impl AttributeFromRaw<'_> for UnknownAttributes {
326    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
327    where
328        Self: Sized,
329    {
330        Self::try_from(raw)
331    }
332}
333
334impl TryFrom<&RawAttribute<'_>> for UnknownAttributes {
335    type Error = StunParseError;
336
337    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
338        if raw.header.atype != Self::TYPE {
339            return Err(StunParseError::WrongAttributeImplementation);
340        }
341        if raw.value.len() % 2 != 0 {
342            /* all attributes are 16-bits */
343            return Err(StunParseError::Truncated {
344                expected: raw.value.len() + 1,
345                actual: raw.value.len(),
346            });
347        }
348        let mut attrs = vec![];
349        for attr in raw.value.chunks_exact(2) {
350            attrs.push(BigEndian::read_u16(attr).into());
351        }
352        Ok(Self { attributes: attrs })
353    }
354}
355impl UnknownAttributes {
356    /// Create a new unknown attributes [`Attribute`]
357    ///
358    /// # Examples
359    ///
360    /// ```
361    /// # use stun_types::attribute::*;
362    /// let unknown = UnknownAttributes::new(&[Username::TYPE]);
363    /// assert!(unknown.has_attribute(Username::TYPE));
364    /// ```
365    pub fn new(attrs: &[AttributeType]) -> Self {
366        Self {
367            attributes: attrs.to_vec(),
368        }
369    }
370
371    /// Add an [`AttributeType`] that is unsupported
372    ///
373    /// # Examples
374    ///
375    /// ```
376    /// # use stun_types::attribute::*;
377    /// let mut unknown = UnknownAttributes::new(&[]);
378    /// unknown.add_attribute(Username::TYPE);
379    /// assert!(unknown.has_attribute(Username::TYPE));
380    /// ```
381    pub fn add_attribute(&mut self, attr: AttributeType) {
382        if !self.has_attribute(attr) {
383            self.attributes.push(attr);
384        }
385    }
386
387    /// Check if an [`AttributeType`] is present
388    ///
389    /// # Examples
390    ///
391    /// ```
392    /// # use stun_types::attribute::*;
393    /// let unknown = UnknownAttributes::new(&[Username::TYPE]);
394    /// assert!(unknown.has_attribute(Username::TYPE));
395    /// ```
396    pub fn has_attribute(&self, attr: AttributeType) -> bool {
397        self.attributes.iter().any(|&a| a == attr)
398    }
399
400    fn write_into_data(&self, dest: &mut [u8]) -> usize {
401        let mut offset = 0;
402        for attr in &self.attributes {
403            BigEndian::write_u16(&mut dest[offset..offset + 2], (*attr).into());
404            offset += 2;
405        }
406        offset
407    }
408}
409
410impl std::fmt::Display for UnknownAttributes {
411    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
412        write!(f, "{}: {:?}", Self::TYPE, self.attributes)
413    }
414}
415
416#[cfg(test)]
417mod tests {
418    use super::*;
419    use crate::attribute::{AlternateServer, Nonce, Realm};
420    use tracing::trace;
421
422    #[test]
423    fn error_code() {
424        let _log = crate::tests::test_init_log();
425        let codes = [
426            300, 301, 400, 401, 403, 420, 437, 438, 440, 441, 442, 443, 486, 487, 500, 508, 699,
427        ];
428        for code in codes.iter().copied() {
429            let reason = ErrorCode::default_reason_for_code(code);
430            let err = ErrorCode::new(code, reason).unwrap();
431            trace!("{err}");
432            assert_eq!(err.code(), code);
433            assert_eq!(err.reason(), reason);
434            let raw = RawAttribute::from(&err);
435            trace!("{raw}");
436            assert_eq!(raw.get_type(), ErrorCode::TYPE);
437            let err2 = ErrorCode::try_from(&raw).unwrap();
438            assert_eq!(err2.code(), code);
439            assert_eq!(err2.reason(), reason);
440
441            let mut dest = vec![0; raw.padded_len()];
442            err.write_into(&mut dest).unwrap();
443            let raw = RawAttribute::from_bytes(&dest).unwrap();
444            let err2 = ErrorCode::try_from(&raw).unwrap();
445            assert_eq!(err2.code(), code);
446            assert_eq!(err2.reason(), reason);
447        }
448    }
449
450    fn error_code_new(code: u16) -> ErrorCode {
451        let reason = ErrorCode::default_reason_for_code(code);
452        ErrorCode::new(code, reason).unwrap()
453    }
454
455    #[test]
456    fn error_code_parse_short() {
457        let _log = crate::tests::test_init_log();
458        let err = error_code_new(420);
459        let raw = RawAttribute::from(&err);
460        // no data
461        let mut data: Vec<_> = raw.into();
462        let len = 0;
463        BigEndian::write_u16(&mut data[2..4], len as u16);
464        assert!(matches!(
465            ErrorCode::try_from(&RawAttribute::from_bytes(data[..len + 4].as_ref()).unwrap()),
466            Err(StunParseError::Truncated {
467                expected: 4,
468                actual: 0
469            })
470        ));
471    }
472
473    #[test]
474    fn error_code_parse_wrong_implementation() {
475        let _log = crate::tests::test_init_log();
476        let err = error_code_new(420);
477        let raw = RawAttribute::from(&err);
478        // provide incorrectly typed data
479        let mut data: Vec<_> = raw.into();
480        BigEndian::write_u16(&mut data[0..2], 0);
481        assert!(matches!(
482            ErrorCode::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
483            Err(StunParseError::WrongAttributeImplementation)
484        ));
485    }
486
487    #[test]
488    fn error_code_parse_out_of_range_code() {
489        let _log = crate::tests::test_init_log();
490        let err = error_code_new(420);
491        let raw = RawAttribute::from(&err);
492        let mut data: Vec<_> = raw.into();
493
494        // write an invalid error code
495        data[6] = 7;
496        assert!(matches!(
497            ErrorCode::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
498            Err(StunParseError::InvalidAttributeData)
499        ));
500    }
501
502    #[test]
503    fn error_code_parse_invalid_reason() {
504        let _log = crate::tests::test_init_log();
505        let err = error_code_new(420);
506        let raw = RawAttribute::from(&err);
507        let mut data: Vec<_> = raw.into();
508
509        // write an invalid utf8 bytes
510        data[10] = 0x88;
511        assert!(matches!(
512            ErrorCode::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
513            Err(StunParseError::InvalidAttributeData)
514        ));
515    }
516
517    #[test]
518    fn error_code_build_default_reason() {
519        let _log = crate::tests::test_init_log();
520        let err = ErrorCode::builder(420).build().unwrap();
521        assert_eq!(err.code(), 420);
522        assert!(err.reason().len() > 0);
523    }
524
525    #[test]
526    fn error_code_build_out_of_range() {
527        let _log = crate::tests::test_init_log();
528        assert!(matches!(
529            ErrorCode::builder(700).build(),
530            Err(StunWriteError::OutOfRange {
531                value: 700,
532                min: _,
533                max: _
534            })
535        ));
536    }
537
538    #[test]
539    fn error_code_new_out_of_range() {
540        let _log = crate::tests::test_init_log();
541        assert!(matches!(
542            ErrorCode::new(700, "some-reason"),
543            Err(StunWriteError::OutOfRange {
544                value: 700,
545                min: _,
546                max: _
547            })
548        ));
549    }
550
551    #[test]
552    fn unknown_attributes() {
553        let _log = crate::tests::test_init_log();
554        let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
555        unknown.add_attribute(AlternateServer::TYPE);
556        // duplicates ignored
557        unknown.add_attribute(AlternateServer::TYPE);
558        trace!("{unknown}");
559        assert!(unknown.has_attribute(Realm::TYPE));
560        assert!(unknown.has_attribute(AlternateServer::TYPE));
561        assert!(!unknown.has_attribute(Nonce::TYPE));
562        let raw = RawAttribute::from(&unknown);
563        assert_eq!(raw.get_type(), UnknownAttributes::TYPE);
564        let unknown2 = UnknownAttributes::try_from(&raw).unwrap();
565        assert!(unknown2.has_attribute(Realm::TYPE));
566        assert!(unknown2.has_attribute(AlternateServer::TYPE));
567        assert!(!unknown2.has_attribute(Nonce::TYPE));
568        // truncate by one byte
569        let mut data: Vec<_> = raw.clone().into();
570        let len = data.len();
571        BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
572        assert!(matches!(
573            UnknownAttributes::try_from(
574                &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
575            ),
576            Err(StunParseError::Truncated {
577                expected: 4,
578                actual: 3
579            })
580        ));
581        // provide incorrectly typed data
582        let mut data: Vec<_> = raw.clone().into();
583        BigEndian::write_u16(&mut data[0..2], 0);
584        assert!(matches!(
585            UnknownAttributes::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
586            Err(StunParseError::WrongAttributeImplementation)
587        ));
588
589        let mut dest = vec![0; raw.padded_len()];
590        unknown.write_into(&mut dest).unwrap();
591        tracing::error!("{dest:?}");
592        let raw = RawAttribute::from_bytes(&dest).unwrap();
593        let unknown2 = UnknownAttributes::try_from(&raw).unwrap();
594        assert!(unknown2.has_attribute(Realm::TYPE));
595        assert!(unknown2.has_attribute(AlternateServer::TYPE));
596    }
597}