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