Skip to main content

stun_types/
lib.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//
9// SPDX-License-Identifier: MIT OR Apache-2.0
10
11#![deny(missing_debug_implementations)]
12#![deny(missing_docs)]
13#![cfg_attr(docsrs, feature(doc_cfg))]
14#![deny(clippy::std_instead_of_core)]
15#![deny(clippy::std_instead_of_alloc)]
16
17//! # stun-types
18//!
19//! An implementation of parsing and writing STUN messages and attributes based on trait
20//! implementations.
21//!
22//! This is based on the following standards:
23//! - [RFC8489] - 'Session Traversal Utilities for NAT (STUN)'
24//! - [RFC5389] - 'Session Traversal Utilities for NAT (STUN)'
25//! - [RFC3489] - 'STUN - Simple Traversal of User Datagram Protocol (UDP)
26//!   Through Network Address Translators (NATs)'
27//!
28//! ## [Message](crate::message::Message)
29//!
30//! ### Message Parsing
31//!
32//! Message parsing is zerocopy by default through the [`RawAttribute`](crate::attribute::RawAttribute)
33//! struct. Converting to a concrete attribute implementation (such as
34//! [`Software`](crate::attribute::Software)) may incur a copy depending on the attribute
35//! implementation.
36//!
37//! ### Message writing
38//!
39//! The destination for a written Message is completely customizable through the
40//! [`MessageWrite`](crate::message::MessageWrite) trait. It is therefore possible to write directly
41//! into network provided buffers for increased performance and throughput.
42//!
43//! [`MessageWriteVec`](crate::message::MessageWriteVec) provides a simple implementation of
44//! message writing that will write into a newly allocated `Vec<u8>`.
45//!
46//! ## [Attribute](crate::attribute::Attribute)
47//!
48//! An [`Attribute`](crate::attribute::Attribute) implementation can be implemented entirely
49//! outside of this crate and used exactly the same as an Attribute implemented within this crate.
50//! Look at [`attribute`] module level documentation for an example of defining your own
51//! [`Attribute`](crate::attribute::Attribute).
52//!
53//! For TURN-related attributes, have a look at the [turn-types] crate which uses this crate to
54//! implement STUN attributes for TURN.
55//!
56//! [RFC8489]: https://tools.ietf.org/html/rfc8489
57//! [RFC5389]: https://tools.ietf.org/html/rfc5389
58//! [RFC3489]: https://tools.ietf.org/html/rfc3489
59//! [turn-types]: https://docs.rs/turn-types/latest/turn_types/
60//!
61//! ## Examples
62//!
63//! See the [`message`] and [`attribute`] module documentation for examples of use.
64
65#![no_std]
66
67use core::{
68    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
69    str::FromStr,
70};
71
72use crate::message::StunParseError;
73
74pub mod attribute;
75pub mod data;
76pub mod message;
77
78extern crate alloc;
79
80#[cfg(any(feature = "std", test))]
81extern crate std;
82
83/// The transport family
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85#[repr(u32)]
86pub enum TransportType {
87    /// The UDP transport
88    Udp,
89    /// The TCP transport
90    Tcp,
91}
92
93/// Errors when parsing a [`TransportType`]
94#[derive(Debug, thiserror::Error)]
95pub enum ParseTransportTypeError {
96    /// An unknown transport value was provided
97    #[error("Unknown transport value was provided")]
98    UnknownTransport,
99}
100
101impl FromStr for TransportType {
102    type Err = ParseTransportTypeError;
103
104    fn from_str(s: &str) -> Result<Self, Self::Err> {
105        if s.eq_ignore_ascii_case("UDP") {
106            Ok(TransportType::Udp)
107        } else if s.eq_ignore_ascii_case("TCP") {
108            Ok(TransportType::Tcp)
109        } else {
110            Err(ParseTransportTypeError::UnknownTransport)
111        }
112    }
113}
114
115impl core::fmt::Display for TransportType {
116    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
117        match &self {
118            TransportType::Udp => f.pad("UDP"),
119            TransportType::Tcp => f.pad("TCP"),
120        }
121    }
122}
123
124/// The address family of a socket
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126pub enum AddressFamily {
127    /// IP version 4 address.
128    IPV4,
129    /// IP version 6 address.
130    IPV6,
131}
132
133impl AddressFamily {
134    pub(crate) fn to_byte(self) -> u8 {
135        match self {
136            AddressFamily::IPV4 => 0x1,
137            AddressFamily::IPV6 => 0x2,
138        }
139    }
140
141    pub(crate) fn from_byte(byte: u8) -> Result<AddressFamily, StunParseError> {
142        match byte {
143            0x1 => Ok(AddressFamily::IPV4),
144            0x2 => Ok(AddressFamily::IPV6),
145            _ => Err(StunParseError::InvalidAttributeData),
146        }
147    }
148}
149
150impl From<&SocketAddr> for AddressFamily {
151    fn from(value: &SocketAddr) -> Self {
152        match value {
153            SocketAddr::V4(_) => Self::IPV4,
154            SocketAddr::V6(_) => Self::IPV6,
155        }
156    }
157}
158
159impl From<&SocketAddrV4> for AddressFamily {
160    fn from(_value: &SocketAddrV4) -> Self {
161        Self::IPV4
162    }
163}
164
165impl From<&SocketAddrV6> for AddressFamily {
166    fn from(_value: &SocketAddrV6) -> Self {
167        Self::IPV6
168    }
169}
170
171impl From<&IpAddr> for AddressFamily {
172    fn from(value: &IpAddr) -> Self {
173        match value {
174            IpAddr::V4(_) => Self::IPV4,
175            IpAddr::V6(_) => Self::IPV6,
176        }
177    }
178}
179
180impl From<&Ipv4Addr> for AddressFamily {
181    fn from(_value: &Ipv4Addr) -> Self {
182        Self::IPV4
183    }
184}
185
186impl From<&Ipv6Addr> for AddressFamily {
187    fn from(_value: &Ipv6Addr) -> Self {
188        Self::IPV6
189    }
190}
191
192impl core::fmt::Display for AddressFamily {
193    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
194        match self {
195            AddressFamily::IPV4 => write!(f, "IPV4"),
196            AddressFamily::IPV6 => write!(f, "IPV6"),
197        }
198    }
199}
200
201/// Prelude module for traits
202pub mod prelude {
203    pub use crate::attribute::{
204        Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeWrite,
205        AttributeWriteExt,
206    };
207    pub use crate::message::{MessageWrite, MessageWriteExt};
208}
209
210#[cfg(test)]
211pub(crate) mod tests {
212    use alloc::borrow::ToOwned;
213    use alloc::format;
214    use alloc::string::{String, ToString};
215    use tracing::subscriber::DefaultGuard;
216    use tracing_subscriber::layer::SubscriberExt;
217    use tracing_subscriber::Layer;
218
219    use super::*;
220
221    pub fn test_init_log() -> DefaultGuard {
222        let level_filter = std::env::var("STUN_LOG")
223            .or(std::env::var("RUST_LOG"))
224            .ok()
225            .and_then(|var| var.parse::<tracing_subscriber::filter::Targets>().ok())
226            .unwrap_or(
227                tracing_subscriber::filter::Targets::new().with_default(tracing::Level::TRACE),
228            );
229        let registry = tracing_subscriber::registry().with(
230            tracing_subscriber::fmt::layer()
231                .with_file(true)
232                .with_line_number(true)
233                .with_level(true)
234                .with_target(false)
235                .with_test_writer()
236                .with_filter(level_filter),
237        );
238        tracing::subscriber::set_default(registry)
239    }
240
241    #[test]
242    fn parse_transport_type() {
243        assert!(matches!("UDP".parse(), Ok(TransportType::Udp)));
244        assert!(matches!("TCP".parse(), Ok(TransportType::Tcp)));
245        assert!(matches!("udp".parse(), Ok(TransportType::Udp)));
246        assert!(matches!("tcp".parse(), Ok(TransportType::Tcp)));
247        assert!(matches!(
248            TransportType::from_str("Random"),
249            Err(ParseTransportTypeError::UnknownTransport)
250        ));
251    }
252
253    #[test]
254    fn transport_type_str() {
255        assert_eq!(TransportType::Udp.to_string(), String::from("UDP"));
256        assert_eq!(TransportType::Tcp.to_string(), String::from("TCP"));
257    }
258
259    #[test]
260    fn address_family() {
261        assert_eq!(AddressFamily::IPV4.to_byte(), 1);
262        assert_eq!(AddressFamily::from_byte(1).unwrap(), AddressFamily::IPV4);
263        assert_eq!(format!("{}", AddressFamily::IPV4), "IPV4".to_owned());
264        assert_eq!(AddressFamily::IPV6.to_byte(), 2);
265        assert_eq!(AddressFamily::from_byte(2).unwrap(), AddressFamily::IPV6);
266        assert_eq!(format!("{}", AddressFamily::IPV6), "IPV6".to_owned());
267        assert!(matches!(
268            AddressFamily::from_byte(3),
269            Err(StunParseError::InvalidAttributeData)
270        ));
271        let ipv4_addr: SocketAddr = "127.0.0.1:1".parse().unwrap();
272        assert_eq!(AddressFamily::from(&ipv4_addr), AddressFamily::IPV4);
273        assert_eq!(AddressFamily::from(&ipv4_addr.ip()), AddressFamily::IPV4);
274        let SocketAddr::V4(ipv4_addr) = ipv4_addr else {
275            unreachable!();
276        };
277        assert_eq!(AddressFamily::from(&ipv4_addr), AddressFamily::IPV4);
278        assert_eq!(AddressFamily::from(ipv4_addr.ip()), AddressFamily::IPV4);
279        let ipv6_addr: SocketAddr = "[::1]:1".parse().unwrap();
280        assert_eq!(AddressFamily::from(&ipv6_addr), AddressFamily::IPV6);
281        assert_eq!(AddressFamily::from(&ipv6_addr.ip()), AddressFamily::IPV6);
282        let SocketAddr::V6(ipv6_addr) = ipv6_addr else {
283            unreachable!();
284        };
285        assert_eq!(AddressFamily::from(&ipv6_addr), AddressFamily::IPV6);
286        assert_eq!(AddressFamily::from(ipv6_addr.ip()), AddressFamily::IPV6);
287    }
288}