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