stun_types/attribute/
alternate.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 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/// The AlternateServer [`Attribute`]
22#[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    /// Create a new AlternateServer [`Attribute`]
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// # use stun_types::attribute::*;
73    /// let addr = "127.0.0.1:12345".parse().unwrap();
74    /// let server = AlternateServer::new(addr);
75    /// assert_eq!(server.server(), addr);
76    /// ```
77    pub fn new(addr: SocketAddr) -> Self {
78        Self {
79            addr: MappedSocketAddr::new(addr),
80        }
81    }
82
83    /// Retrieve the server value
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// # use stun_types::attribute::*;
89    /// let addr = "127.0.0.1:12345".parse().unwrap();
90    /// let server = AlternateServer::new(addr);
91    /// assert_eq!(server.server(), addr);
92    /// ```
93    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/// The AlternateDomain [`Attribute`]
105#[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        // FIXME: should be ascii-only
136        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    /// Create a new AlternateDomain [`Attribute`]
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// # use stun_types::attribute::*;
165    /// let dns = "example.com";
166    /// let domain = AlternateDomain::new(dns);
167    /// assert_eq!(domain.domain(), dns);
168    /// ```
169    pub fn new(domain: &str) -> Self {
170        Self {
171            domain: domain.to_string(),
172        }
173    }
174
175    /// Retrieve the domain value
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// # use stun_types::attribute::*;
181    /// let dns = "example.com";
182    /// let domain = AlternateDomain::new(dns);
183    /// assert_eq!(domain.domain(), dns);
184    /// ```
185    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            // truncate by one byte
225            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            // provide incorrectly typed data
238            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        // provide incorrectly typed data
267        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        // invalid utf-8 data
275        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}