stun_types/attribute/
alternate.rs1use alloc::borrow::ToOwned;
10use alloc::string::{String, ToString};
11use core::convert::TryFrom;
12use core::net::SocketAddr;
13
14use crate::message::StunParseError;
15
16use super::{
17 Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
18 AttributeWriteExt, MappedSocketAddr, RawAttribute,
19};
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct AlternateServer {
24 addr: MappedSocketAddr,
25}
26
27impl AttributeStaticType for AlternateServer {
28 const TYPE: AttributeType = AttributeType(0x8023);
29}
30impl Attribute for AlternateServer {
31 fn get_type(&self) -> AttributeType {
32 Self::TYPE
33 }
34
35 fn length(&self) -> u16 {
36 self.addr.length()
37 }
38}
39
40impl AttributeWrite for AlternateServer {
41 fn to_raw(&self) -> RawAttribute<'_> {
42 self.addr.to_raw(AlternateServer::TYPE)
43 }
44 fn write_into_unchecked(&self, dest: &mut [u8]) {
45 self.write_header_unchecked(dest);
46 self.addr.write_into_unchecked(&mut dest[4..]);
47 }
48}
49
50impl AttributeFromRaw<'_> for AlternateServer {
51 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError> {
52 Self::try_from(raw)
53 }
54}
55
56impl TryFrom<&RawAttribute<'_>> for AlternateServer {
57 type Error = StunParseError;
58
59 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
60 raw.check_type_and_len(Self::TYPE, ..)?;
61 let addr = MappedSocketAddr::from_raw(raw)?;
62 Ok(Self { addr })
63 }
64}
65
66impl AlternateServer {
67 pub fn new(addr: SocketAddr) -> Self {
78 Self {
79 addr: MappedSocketAddr::new(addr),
80 }
81 }
82
83 pub fn server(&self) -> SocketAddr {
94 self.addr.addr()
95 }
96}
97
98impl core::fmt::Display for AlternateServer {
99 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
100 write!(f, "{}: {}", AlternateServer::TYPE, self.addr)
101 }
102}
103
104#[derive(Debug, Clone, PartialEq, Eq)]
106pub struct AlternateDomain {
107 domain: String,
108}
109
110impl AttributeStaticType for AlternateDomain {
111 const TYPE: AttributeType = AttributeType(0x8003);
112}
113
114impl Attribute for AlternateDomain {
115 fn get_type(&self) -> AttributeType {
116 Self::TYPE
117 }
118 fn length(&self) -> u16 {
119 self.domain.len() as u16
120 }
121}
122impl AttributeFromRaw<'_> for AlternateDomain {
123 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
124 where
125 Self: Sized,
126 {
127 Self::try_from(raw)
128 }
129}
130impl TryFrom<&RawAttribute<'_>> for AlternateDomain {
131 type Error = StunParseError;
132
133 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
134 raw.check_type_and_len(Self::TYPE, ..)?;
135 Ok(Self {
137 domain: core::str::from_utf8(&raw.value)
138 .map_err(|_| StunParseError::InvalidAttributeData)?
139 .to_owned(),
140 })
141 }
142}
143impl AttributeWrite for AlternateDomain {
144 fn to_raw(&self) -> RawAttribute<'_> {
145 RawAttribute::new(AlternateDomain::TYPE, self.domain.as_bytes())
146 }
147 fn write_into_unchecked(&self, dest: &mut [u8]) {
148 let len = self.padded_len();
149 self.write_header_unchecked(dest);
150 dest[4..4 + self.domain.len()].copy_from_slice(self.domain.as_bytes());
151 let offset = 4 + self.domain.len();
152 if len > offset {
153 dest[offset..len].fill(0);
154 }
155 }
156}
157
158impl AlternateDomain {
159 pub fn new(domain: &str) -> Self {
170 Self {
171 domain: domain.to_string(),
172 }
173 }
174
175 pub fn domain(&self) -> &str {
186 &self.domain
187 }
188}
189
190impl core::fmt::Display for AlternateDomain {
191 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
192 write!(f, "{}: {}", AlternateDomain::TYPE, self.domain)
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use alloc::vec;
200 use alloc::vec::Vec;
201 use byteorder::{BigEndian, ByteOrder};
202 use tracing::trace;
203
204 #[test]
205 fn alternate_server() {
206 let _log = crate::tests::test_init_log();
207 let addrs = &[
208 "192.168.0.1:40000".parse().unwrap(),
209 "[fd12:3456:789a:1::1]:41000".parse().unwrap(),
210 ];
211 for addr in addrs {
212 let mapped = AlternateServer::new(*addr);
213 trace!("{mapped}");
214 assert_eq!(mapped.server(), *addr);
215 match addr {
216 SocketAddr::V4(_) => assert_eq!(mapped.length(), 8),
217 SocketAddr::V6(_) => assert_eq!(mapped.length(), 20),
218 }
219 let raw = RawAttribute::from(&mapped);
220 trace!("{raw}");
221 assert_eq!(raw.get_type(), AlternateServer::TYPE);
222 let mapped2 = AlternateServer::try_from(&raw).unwrap();
223 assert_eq!(mapped2.server(), *addr);
224 let mut data: Vec<_> = raw.clone().into();
226 let len = data.len();
227 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
228 assert!(matches!(
229 AlternateServer::try_from(
230 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
231 ),
232 Err(StunParseError::Truncated {
233 expected: _,
234 actual: _
235 })
236 ));
237 let mut data: Vec<_> = raw.clone().into();
239 BigEndian::write_u16(&mut data[0..2], 0);
240 assert!(matches!(
241 AlternateServer::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
242 Err(StunParseError::WrongAttributeImplementation)
243 ));
244
245 let mut dest = vec![0; raw.padded_len()];
246 mapped.write_into(&mut dest).unwrap();
247 let raw = RawAttribute::from_bytes(&dest).unwrap();
248 let mapped2 = AlternateServer::try_from(&raw).unwrap();
249 assert_eq!(mapped2.server(), *addr);
250 }
251 }
252
253 #[test]
254 fn alternative_domain() {
255 let _log = crate::tests::test_init_log();
256 let dns = "example.com";
257 let attr = AlternateDomain::new(dns);
258 trace!("{attr}");
259 assert_eq!(attr.domain(), dns);
260 assert_eq!(attr.length() as usize, dns.len());
261 let raw = RawAttribute::from(&attr);
262 trace!("{raw}");
263 assert_eq!(raw.get_type(), AlternateDomain::TYPE);
264 let mapped2 = AlternateDomain::try_from(&raw).unwrap();
265 assert_eq!(mapped2.domain(), dns);
266 let mut data: Vec<_> = raw.clone().into();
268 BigEndian::write_u16(&mut data[0..2], 0);
269 assert!(matches!(
270 AlternateDomain::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
271 Err(StunParseError::WrongAttributeImplementation)
272 ));
273 let mut data: Vec<_> = raw.clone().into();
274 data[8] = 0x88;
276 assert!(matches!(
277 AlternateDomain::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
278 Err(StunParseError::InvalidAttributeData)
279 ));
280
281 let mut dest = vec![0; raw.padded_len()];
282 attr.write_into(&mut dest).unwrap();
283 let raw = RawAttribute::from_bytes(&dest).unwrap();
284 let mapped2 = AlternateDomain::try_from(&raw).unwrap();
285 assert_eq!(mapped2.domain(), dns);
286 }
287}