stun_rs/attributes/mobility/
mobility_ticket.rs

1use std::sync::Arc;
2
3use crate::attributes::{stunt_attribute, DecodeAttributeValue, EncodeAttributeValue};
4use crate::common::check_buffer_boundaries;
5use crate::context::{AttributeDecoderContext, AttributeEncoderContext};
6use crate::error::StunError;
7
8const MOBILITY_TICKET: u16 = 0x8030;
9
10/// The [`MobilityTicket`] attribute is used in order to retain an
11/// allocation on the TURN server. It is exchanged between the client
12/// and server to aid mobility.  The value of the MOBILITY-TICKET is
13/// encrypted and is of variable length.
14///
15/// # Examples
16/// ```rust
17/// # use stun_rs::attributes::mobility::MobilityTicket;
18/// // Create a mobility-ticket attribute using an opaque value
19/// let attr = MobilityTicket::new([0x1, 0x2, 0x3, 0x4, 0x5]);
20/// assert_eq!(attr.value().len(), 5);
21/// assert_eq!(attr, [0x1, 0x2, 0x3, 0x4, 0x5]);
22///```
23#[derive(Debug, PartialEq, Eq, Clone)]
24pub struct MobilityTicket(Arc<Vec<u8>>);
25
26impl MobilityTicket {
27    /// Creates a new [`MobilityTicket`] attribute.
28    /// # Arguments
29    /// - `ticket`: The ticket
30    /// # Returns
31    /// The [`MobilityTicket`] attribute
32    pub fn new<T>(ticket: T) -> Self
33    where
34        T: AsRef<[u8]>,
35    {
36        let vec = ticket.as_ref().to_vec();
37        Self(Arc::new(vec))
38    }
39
40    /// Returns the value of the [`MobilityTicket`] attribute
41    pub fn value(&self) -> &[u8] {
42        self.0.as_slice()
43    }
44}
45
46impl<const N: usize> PartialEq<[u8; N]> for MobilityTicket {
47    fn eq(&self, other: &[u8; N]) -> bool {
48        self.value() == other
49    }
50}
51
52impl AsRef<[u8]> for MobilityTicket {
53    fn as_ref(&self) -> &[u8] {
54        self.0.as_ref()
55    }
56}
57
58impl From<&[u8]> for MobilityTicket {
59    fn from(value: &[u8]) -> Self {
60        MobilityTicket::new(value)
61    }
62}
63
64impl crate::attributes::AsVerifiable for MobilityTicket {}
65
66impl DecodeAttributeValue for MobilityTicket {
67    fn decode(ctx: AttributeDecoderContext) -> Result<(Self, usize), StunError> {
68        let raw_value = ctx.raw_value();
69
70        Ok((MobilityTicket::new(raw_value), raw_value.len()))
71    }
72}
73
74impl EncodeAttributeValue for MobilityTicket {
75    fn encode(&self, mut ctx: AttributeEncoderContext) -> Result<usize, StunError> {
76        let ticket = self.value();
77        let ticket_len = ticket.len();
78        let buffer = ctx.raw_value_mut();
79        check_buffer_boundaries(buffer, ticket_len)?;
80        buffer[..ticket_len].clone_from_slice(self.value());
81
82        Ok(ticket_len)
83    }
84}
85
86stunt_attribute!(MobilityTicket, MOBILITY_TICKET);
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::error::StunErrorType;
92    use crate::StunAttribute;
93
94    #[test]
95    fn constructor() {
96        let ticket = MobilityTicket::new([0x0; 0]);
97        assert_eq!(ticket.value().len(), 0);
98
99        let ticket = MobilityTicket::new([0x1; 1]);
100        assert_eq!(ticket.value().len(), 1);
101        assert_eq!(ticket, [0x1; 1]);
102
103        let ticket = MobilityTicket::new([0x1; 10]);
104        assert_eq!(ticket.value().len(), 10);
105        assert_eq!(ticket, [0x1; 10]);
106        assert_eq!(ticket.as_ref(), [0x1; 10]);
107
108        let ticket2 = MobilityTicket::from(ticket.value());
109        assert_eq!(ticket, ticket2);
110
111        let ticket2 = MobilityTicket::new([0x2; 5]);
112        assert_ne!(ticket, ticket2);
113    }
114
115    #[test]
116    fn decode_mobility_ticket_value() {
117        let dummy_msg = [];
118        let buffer = [];
119        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
120
121        let (mobility_ticket, size) =
122            MobilityTicket::decode(ctx).expect("Can not decode MOBILITY-TICKET");
123        assert_eq!(size, 0);
124        assert_eq!(mobility_ticket.value().len(), 0);
125
126        let buffer = [0x01];
127        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
128
129        let (mobility_ticket, size) =
130            MobilityTicket::decode(ctx).expect("Can not decode MOBILITY-TICKET");
131        assert_eq!(size, 1);
132        assert_eq!(mobility_ticket.value().len(), 1);
133        assert_eq!(mobility_ticket, [0x1]);
134
135        let buffer = [0x1, 0x2, 0x3, 0x4, 0x5];
136        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
137
138        let (mobility_ticket, size) =
139            MobilityTicket::decode(ctx).expect("Can not decode MOBILITY-TICKET");
140        assert_eq!(size, 5);
141        assert_eq!(mobility_ticket.value().len(), 5);
142        assert_eq!(mobility_ticket, buffer);
143    }
144
145    #[test]
146    fn encode_mobility_ticket_value() {
147        let dummy_msg: [u8; 0] = [0x0; 0];
148        let ticket = MobilityTicket::new([0x0; 0]);
149        let mut buffer = [];
150        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
151
152        let size = ticket.encode(ctx).expect("Can not encode MOBILITY-TICKET");
153        assert_eq!(size, 0);
154
155        let ticket = MobilityTicket::new([0x1; 1]);
156        let mut buffer = [0x1];
157        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
158
159        let size = ticket.encode(ctx).expect("Can not encode MOBILITY-TICKET");
160        assert_eq!(size, 1);
161        assert_eq!(buffer, [0x1]);
162
163        let ticket = MobilityTicket::new([0x1; 10]);
164        let mut buffer = [0x1; 10];
165        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
166
167        let size = ticket.encode(ctx).expect("Can not encode MOBILITY-TICKET");
168        assert_eq!(size, 10);
169        assert_eq!(buffer, [0x1; 10]);
170
171        let value = [0x1, 0x2, 0x3, 0x4, 0x5];
172        let ticket = MobilityTicket::new(value);
173        let mut buffer = [0x0; 5];
174        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
175
176        let size = ticket.encode(ctx).expect("Can not encode MOBILITY-TICKET");
177        assert_eq!(size, 5);
178        assert_eq!(buffer, value);
179
180        let ticket = MobilityTicket::new([0x1; 10]);
181        let mut buffer = [0x0; 5];
182        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
183
184        let result = ticket.encode(ctx);
185        assert_eq!(
186            result.expect_err("Error expected"),
187            StunErrorType::SmallBuffer
188        );
189    }
190
191    #[test]
192    fn mobility_stun_attribute() {
193        let attr = StunAttribute::MobilityTicket(MobilityTicket::new([0x1; 10]));
194        assert!(attr.is_mobility_ticket());
195        assert!(attr.as_mobility_ticket().is_ok());
196        assert!(attr.as_unknown().is_err());
197
198        assert!(!attr.attribute_type().is_comprehension_required());
199        assert!(attr.attribute_type().is_comprehension_optional());
200
201        let dbg_fmt = format!("{:?}", attr);
202        assert_eq!(
203            "MobilityTicket(MobilityTicket([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]))",
204            dbg_fmt
205        );
206    }
207}