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