turn_types/attribute/
address.rs

1// Copyright (C) 2025 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::net::SocketAddr;
10
11use stun_types::{
12    attribute::*,
13    message::{StunParseError, TransactionId},
14};
15
16/// The [`XorPeerAddress`] [`Attribute`].
17///
18/// Typically used for signalling the address of the peer that a TURN server should send data
19/// towards, or has received data from.
20///
21/// Reference: [RFC5766 Section 14.3](https://datatracker.ietf.org/doc/html/rfc5766#section-14.3).
22#[derive(Debug, Clone)]
23pub struct XorPeerAddress {
24    // stored XOR-ed as we need the transaction id to get the original value
25    addr: XorSocketAddr,
26}
27
28impl AttributeStaticType for XorPeerAddress {
29    const TYPE: AttributeType = AttributeType::new(0x0012);
30}
31
32impl Attribute for XorPeerAddress {
33    fn get_type(&self) -> AttributeType {
34        Self::TYPE
35    }
36
37    fn length(&self) -> u16 {
38        self.addr.length()
39    }
40}
41
42impl AttributeWrite for XorPeerAddress {
43    fn to_raw(&self) -> RawAttribute<'_> {
44        self.addr.to_raw(self.get_type())
45    }
46    fn write_into_unchecked(&self, dest: &mut [u8]) {
47        self.write_header_unchecked(dest);
48        self.addr.write_into_unchecked(&mut dest[4..]);
49    }
50}
51
52impl AttributeFromRaw<'_> for XorPeerAddress {
53    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
54    where
55        Self: Sized,
56    {
57        Self::try_from(raw)
58    }
59}
60
61impl TryFrom<&RawAttribute<'_>> for XorPeerAddress {
62    type Error = StunParseError;
63    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
64        raw.check_type_and_len(Self::TYPE, 4..=20)?;
65        Ok(Self {
66            addr: XorSocketAddr::from_raw(raw)?,
67        })
68    }
69}
70
71impl XorPeerAddress {
72    /// Create a new [`XorPeerAddress`] [`Attribute`].
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// # use turn_types::attribute::*;
78    /// # use std::net::SocketAddr;
79    /// let addr = "127.0.0.1:1234".parse().unwrap();
80    /// let mapped_addr = XorPeerAddress::new(addr, 0x5678.into());
81    /// assert_eq!(mapped_addr.addr(0x5678.into()), addr);
82    /// ```
83    pub fn new(addr: SocketAddr, transaction: TransactionId) -> Self {
84        Self {
85            addr: XorSocketAddr::new(addr, transaction),
86        }
87    }
88
89    /// Retrieve the address stored in a [`XorPeerAddress`].
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// # use turn_types::attribute::*;
95    /// # use std::net::SocketAddr;
96    /// let addr = "[::1]:1234".parse().unwrap();
97    /// let mapped_addr = XorPeerAddress::new(addr, 0x5678.into());
98    /// assert_eq!(mapped_addr.addr(0x5678.into()), addr);
99    /// ```
100    pub fn addr(&self, transaction: TransactionId) -> SocketAddr {
101        self.addr.addr(transaction)
102    }
103}
104
105impl std::fmt::Display for XorPeerAddress {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(f, "{}: {}", self.get_type(), self.addr)
108    }
109}
110
111/// The [`XorRelayedAddress`] [`Attribute`].
112///
113/// Used to reference the address allocated on the TURN server on behalf of the client.
114///
115/// Reference: [RFC5766 Section 14.5](https://datatracker.ietf.org/doc/html/rfc5766#section-14.5).
116#[derive(Debug, Clone)]
117pub struct XorRelayedAddress {
118    addr: XorSocketAddr,
119}
120
121impl AttributeStaticType for XorRelayedAddress {
122    const TYPE: AttributeType = AttributeType::new(0x0016);
123}
124
125impl Attribute for XorRelayedAddress {
126    fn get_type(&self) -> AttributeType {
127        Self::TYPE
128    }
129
130    fn length(&self) -> u16 {
131        self.addr.length()
132    }
133}
134
135impl AttributeWrite for XorRelayedAddress {
136    fn to_raw(&self) -> RawAttribute<'_> {
137        self.addr.to_raw(self.get_type())
138    }
139    fn write_into_unchecked(&self, dest: &mut [u8]) {
140        self.write_header_unchecked(dest);
141        self.addr.write_into_unchecked(&mut dest[4..]);
142    }
143}
144
145impl AttributeFromRaw<'_> for XorRelayedAddress {
146    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
147    where
148        Self: Sized,
149    {
150        Self::try_from(raw)
151    }
152}
153
154impl TryFrom<&RawAttribute<'_>> for XorRelayedAddress {
155    type Error = StunParseError;
156    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
157        if raw.get_type() != Self::TYPE {
158            return Err(StunParseError::WrongAttributeImplementation);
159        }
160        Ok(Self {
161            addr: XorSocketAddr::from_raw(raw)?,
162        })
163    }
164}
165
166impl XorRelayedAddress {
167    /// Create a new [`XorRelayedAddress`] [`Attribute`].
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// # use turn_types::attribute::*;
173    /// # use std::net::SocketAddr;
174    /// let addr = "127.0.0.1:1234".parse().unwrap();
175    /// let mapped_addr = XorRelayedAddress::new(addr, 0x5678.into());
176    /// assert_eq!(mapped_addr.addr(0x5678.into()), addr);
177    /// ```
178    pub fn new(addr: SocketAddr, transaction: TransactionId) -> Self {
179        Self {
180            addr: XorSocketAddr::new(addr, transaction),
181        }
182    }
183
184    /// Retrieve the address stored in a [`XorRelayedAddress`].
185    ///
186    /// # Examples
187    ///
188    /// ```
189    /// # use turn_types::attribute::*;
190    /// # use std::net::SocketAddr;
191    /// let addr = "[::1]:1234".parse().unwrap();
192    /// let mapped_addr = XorRelayedAddress::new(addr, 0x5678.into());
193    /// assert_eq!(mapped_addr.addr(0x5678.into()), addr);
194    /// ```
195    pub fn addr(&self, transaction: TransactionId) -> SocketAddr {
196        self.addr.addr(transaction)
197    }
198}
199
200impl std::fmt::Display for XorRelayedAddress {
201    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202        write!(f, "{}: {}", self.get_type(), self.addr)
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::*;
209    use byteorder::{BigEndian, ByteOrder};
210
211    #[test]
212    fn xor_peer_address() {
213        let _log = crate::tests::test_init_log();
214        let transaction_id = 0x9876_5432_1098_7654_3210_9876.into();
215        let addrs = &[
216            "192.168.0.1:40000".parse().unwrap(),
217            "[fd12:3456:789a:1::1]:41000".parse().unwrap(),
218        ];
219        for addr in addrs {
220            let mapped = XorPeerAddress::new(*addr, transaction_id);
221            assert_eq!(mapped.get_type(), XorPeerAddress::TYPE);
222            assert_eq!(mapped.addr(transaction_id), *addr);
223            let raw: RawAttribute = mapped.to_raw();
224            println!("{}", raw);
225            assert_eq!(raw.get_type(), XorPeerAddress::TYPE);
226            let mapped2 = XorPeerAddress::try_from(&raw).unwrap();
227            assert_eq!(mapped2.get_type(), XorPeerAddress::TYPE);
228            assert_eq!(mapped2.addr(transaction_id), *addr);
229            // truncate by one byte
230            let mut data: Vec<_> = raw.clone().into();
231            let len = data.len();
232            BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
233            assert!(matches!(
234                XorPeerAddress::try_from(
235                    &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
236                ),
237                Err(StunParseError::Truncated {
238                    expected: _,
239                    actual: _,
240                })
241            ));
242            // provide incorrectly typed data
243            let mut data: Vec<_> = raw.into();
244            BigEndian::write_u16(&mut data[0..2], 0);
245            assert!(matches!(
246                XorPeerAddress::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
247                Err(StunParseError::WrongAttributeImplementation)
248            ));
249        }
250    }
251
252    #[test]
253    fn xor_relayed_address() {
254        let _log = crate::tests::test_init_log();
255        let transaction_id = 0x9876_5432_1098_7654_3210_9876.into();
256        let addrs = &[
257            "192.168.0.1:40000".parse().unwrap(),
258            "[fd12:3456:789a:1::1]:41000".parse().unwrap(),
259        ];
260        for addr in addrs {
261            let mapped = XorRelayedAddress::new(*addr, transaction_id);
262            assert_eq!(mapped.get_type(), XorRelayedAddress::TYPE);
263            assert_eq!(mapped.addr(transaction_id), *addr);
264            let raw: RawAttribute = mapped.to_raw();
265            println!("{}", raw);
266            assert_eq!(raw.get_type(), XorRelayedAddress::TYPE);
267            let mapped2 = XorRelayedAddress::try_from(&raw).unwrap();
268            assert_eq!(mapped2.get_type(), XorRelayedAddress::TYPE);
269            assert_eq!(mapped2.addr(transaction_id), *addr);
270            // truncate by one byte
271            let mut data: Vec<_> = raw.clone().into();
272            let len = data.len();
273            BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
274            assert!(matches!(
275                XorRelayedAddress::try_from(
276                    &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
277                ),
278                Err(StunParseError::Truncated {
279                    expected: _,
280                    actual: _,
281                })
282            ));
283            // provide incorrectly typed data
284            let mut data: Vec<_> = raw.into();
285            BigEndian::write_u16(&mut data[0..2], 0);
286            assert!(matches!(
287                XorRelayedAddress::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
288                Err(StunParseError::WrongAttributeImplementation)
289            ));
290        }
291    }
292}