stun_rs/
lib.rs

1//! STUN library.
2//!
3//! This crate provides a simple framework to manage STUN protocol.
4//! The implementation is based on:
5//! * [`RFC8489`](https://datatracker.ietf.org/doc/html/rfc8489). Session Traversal Utilities for NAT (STUN).
6//! * [`RFC8445`](https://datatracker.ietf.org/doc/html/rfc8445). Interactive Connectivity Establishment (ICE).
7//! * [`RFC8656`](https://datatracker.ietf.org/doc/html/rfc8656). Traversal Using Relays around NAT (TURN).
8//! * [`RFC5769`](https://datatracker.ietf.org/doc/html/rfc5769). Test Vectors for Session Traversal Utilities for NAT (STUN).
9//! * [`RFC8016`](https://datatracker.ietf.org/doc/html/rfc8016). Mobility with Traversal Using Relays around NAT (TURN).
10//!
11//! # Usage
12//! Example that creates and encodes a STUN Binding request
13//!```rust
14//! # use stun_rs::attributes::stun::{MessageIntegrity, Nonce, Realm, UserName};
15//! # use stun_rs::{Algorithm, AlgorithmId, MessageEncoderBuilder, HMACKey,
16//! #  MessageClass, MessageMethod, StunAttribute, StunMessage, StunMessageBuilder,
17//! # };
18//! # use stun_rs::methods::BINDING;
19//! # use std::convert::TryFrom;
20//! # use std::error::Error;
21//! #
22//! # fn main() -> Result<(), Box<dyn Error>> {
23//! // Create attributes
24//! let username = UserName::new("\u{30DE}\u{30C8}\u{30EA}\u{30C3}\u{30AF}\u{30B9}")?;
25//! let nonce = Nonce::new("f//499k954d6OL34oL9FSTvy64sA")?;
26//! let realm = Realm::new("example.org")?;
27//! let password = "TheMatrIX";
28//! let algorithm = Algorithm::from(AlgorithmId::MD5);
29//! let key = HMACKey::new_long_term(&username, &realm, password, algorithm)?;
30//! let integrity = MessageIntegrity::new(key);
31//!
32//! // Create the message
33//! let msg = StunMessageBuilder::new(
34//!   BINDING,
35//!   MessageClass::Request,
36//! )
37//! .with_attribute(username)
38//! .with_attribute(nonce)
39//! .with_attribute(realm)
40//! .with_attribute(integrity)
41//! .build();
42//!
43//! // Create an encoder to encode the message into a buffer
44//! let encoder = MessageEncoderBuilder::default().build();
45//! let mut buffer: [u8; 150] = [0x00; 150];
46//! let size = encoder.encode(&mut buffer, &msg)?;
47//! assert_eq!(size, 116);
48//! #
49//! #   Ok(())
50//! # }
51//!```
52//!
53//! Example that decodes a STUN Binding response and fetches some attributes.
54//!```rust
55//! # use stun_rs::attributes::stun::{Software, XorMappedAddress};
56//! # use stun_rs::{DecoderContextBuilder, HMACKey, MessageClass,
57//! #  MessageDecoderBuilder, StunMessage};
58//! # use stun_rs::methods::BINDING;
59//! # use std::net::{IpAddr, Ipv4Addr};
60//! # use std::error::Error;
61//! #
62//! # fn main() -> Result<(), Box<dyn Error>> {
63//! // This response uses the following parameter:
64//! // Password: `VOkJxbRl1RmTxUk/WvJxBt` (without quotes)
65//! // Software name: "test vector" (without quotes)
66//! // Mapped address: 192.0.2.1 port 32853
67//! let sample_ipv4_response = [
68//!     0x01, 0x01, 0x00, 0x3c, // Response type and message length
69//!     0x21, 0x12, 0xa4, 0x42, // Magic cookie
70//!     0xb7, 0xe7, 0xa7, 0x01, // }
71//!     0xbc, 0x34, 0xd6, 0x86, // }  Transaction ID
72//!     0xfa, 0x87, 0xdf, 0xae, // }
73//!     0x80, 0x22, 0x00, 0x0b, // SOFTWARE attribute header
74//!     0x74, 0x65, 0x73, 0x74, // }
75//!     0x20, 0x76, 0x65, 0x63, // }  UTF-8 server name
76//!     0x74, 0x6f, 0x72, 0x20, // }
77//!     0x00, 0x20, 0x00, 0x08, // XOR-MAPPED-ADDRESS attribute header
78//!     0x00, 0x01, 0xa1, 0x47, // Address family (IPv4) and xor'd mapped port number
79//!     0xe1, 0x12, 0xa6, 0x43, // Xor'd mapped IPv4 address
80//!     0x00, 0x08, 0x00, 0x14, // MESSAGE-INTEGRITY header
81//!     0x2b, 0x91, 0xf5, 0x99, // }
82//!     0xfd, 0x9e, 0x90, 0xc3, // }
83//!     0x8c, 0x74, 0x89, 0xf9, // } HMAC-SHA1 fingerprint
84//!     0x2a, 0xf9, 0xba, 0x53, // }
85//!     0xf0, 0x6b, 0xe7, 0xd7, // }
86//!     0x80, 0x28, 0x00, 0x04, // FINGERPRINT attribute header
87//!     0xc0, 0x7d, 0x4c, 0x96, // Reserved for CRC32 fingerprint
88//! ];
89//!
90//! // Create a STUN decoder context using the password as a short credential
91//! // mechanism and force validation of MESSAGE-INTEGRITY and FINGERPRINT
92//! let ctx = DecoderContextBuilder::default()
93//!   .with_key(
94//!     HMACKey::new_short_term("VOkJxbRl1RmTxUk/WvJxBt")?,
95//!   )
96//!   .with_validation()
97//!   .build();
98//! let decoder = MessageDecoderBuilder::default().with_context(ctx).build();
99//!
100//! let (msg, size) = decoder.decode(&sample_ipv4_response)?;
101//! assert_eq!(size, sample_ipv4_response.len());
102//!
103//! // Check message method is a BINDING response
104//! assert_eq!(msg.method(), BINDING);
105//! assert_eq!(msg.class(), MessageClass::SuccessResponse);
106//!
107//! let software = msg.get::<Software>()
108//!   .ok_or("Software attribute not found")?
109//!   .as_software()?;
110//! assert_eq!(software, "test vector");
111//!
112//! let xor_addr = msg.get::<XorMappedAddress>()
113//!   .ok_or("XorMappedAddress attribute not found")?
114//!   .as_xor_mapped_address()?;
115//! let socket = xor_addr.socket_address();
116//! assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1)));
117//! assert_eq!(socket.port(), 32853);
118//! assert!(socket.is_ipv4());
119//! #
120//! #   Ok(())
121//! # }
122//!```
123//!
124//! #  Common features
125//! This crate defines next feature flags that can be enabled:
126//! * **turn**: Extends support for parsing attributes defined in
127//!     [`RFC8656`](https://datatracker.ietf.org/doc/html/rfc8656).
128//!     Traversal Using Relays around NAT (TURN).
129//! * **ice**: Extends support for parsing attributes defined in
130//!     [`RFC8445`](https://datatracker.ietf.org/doc/html/rfc8445).
131//!     Interactive Connectivity Establishment (ICE).
132//! * **mobility**: Extends support for parsing attributes defined in
133//!     [`RFC8016`](https://datatracker.ietf.org/doc/html/rfc8016).
134//!     Mobility with Traversal Using Relays around NAT (TURN).
135//! * **experiments**: This flag can be set to adjust some behavior
136//!     of the library, such as default padding. When testing protocols,
137//!     we can use this flag to force the library to keep the data
138//!     associated with [Unknown](crate::attributes::Unknown) attributes.
139//!     By default, [Unknown](crate::attributes::Unknown) attributes
140//!     store no data to save memory consumption.
141
142#![deny(missing_docs)]
143
144mod algorithm;
145mod common;
146mod context;
147mod message;
148mod raw;
149mod registry;
150mod strings;
151mod types;
152
153pub mod attributes;
154pub mod error;
155pub mod methods;
156
157#[cfg(feature = "turn")]
158pub mod protocols;
159
160#[cfg(feature = "experiments")]
161pub use crate::context::StunPadding;
162
163pub use crate::algorithm::{Algorithm, AlgorithmId};
164pub use crate::attributes::{AttributeType, StunAttribute, StunAttributeType};
165pub use crate::context::{
166    DecoderContext, DecoderContextBuilder, MessageDecoder, MessageDecoderBuilder,
167};
168pub use crate::context::{
169    EncoderContext, EncoderContextBuilder, MessageEncoder, MessageEncoderBuilder,
170};
171pub use crate::error::{StunError, StunErrorType};
172pub use crate::message::{
173    MessageClass, MessageMethod, MessageType, StunMessage, StunMessageBuilder,
174};
175pub use crate::raw::{MessageHeader, MESSAGE_HEADER_SIZE};
176pub use crate::types::{
177    AddressFamily, Cookie, CredentialMechanism, ErrorCode, HMACKey, TransactionId, MAGIC_COOKIE,
178};
179
180/// Provides a simple interface to encode elements into buffers.
181pub(crate) trait Encode {
182    /// Encodes an object in binary using network-oriented format.
183    /// # Arguments:
184    /// - `buffer`- output buffer where the data will be serialized.
185    /// # Returns:
186    /// The size in bytes taken by the serialized object or
187    /// a [`StunError`] describing the error.
188    fn encode(&self, buffer: &mut [u8]) -> Result<usize, StunError>;
189}
190
191/// Provides a simple interface to decode elements from buffers.
192pub(crate) trait Decode<'a> {
193    /// Decodes an object serialized in binary from a buffer.
194    /// # Arguments:
195    /// - `buffer`: input buffer were the object is encoded.
196    /// # Returns:
197    /// The object or a [`StunError`] describing the error.
198    fn decode(buffer: &'a [u8]) -> Result<(Self, usize), StunError>
199    where
200        Self: Sized;
201}
202
203/// Gets the input text used by attributes that requires validation.
204/// The text used as input for validation is the STUN message,
205/// up to and including the attribute preceding the specified attribute.
206/// The Length field of the STUN message header is adjusted to
207/// point to the end of the value of this attribute.
208///
209/// # Examples
210///```rust
211/// # use stun_rs::{get_input_text, MessageDecoderBuilder};
212/// # use stun_rs::attributes::stun::{Fingerprint, MessageIntegrity, MessageIntegritySha256};
213/// # use std::error::Error;
214/// #
215/// # fn main() -> Result<(), Box<dyn Error>> {
216/// // Sample buffer
217/// let sample_ipv4_response = [
218///     0x01, 0x01, 0x00, 0x3c, // Response type and message length
219///     0x21, 0x12, 0xa4, 0x42, // Magic cookie
220///     0xb7, 0xe7, 0xa7, 0x01, // }
221///     0xbc, 0x34, 0xd6, 0x86, // }  Transaction ID
222///     0xfa, 0x87, 0xdf, 0xae, // }
223///     0x80, 0x22, 0x00, 0x0b, // SOFTWARE attribute header
224///     0x74, 0x65, 0x73, 0x74, // }
225///     0x20, 0x76, 0x65, 0x63, // }  UTF-8 server name (1 byte padding)
226///     0x74, 0x6f, 0x72, 0x20, // }
227///     0x00, 0x20, 0x00, 0x08, // XOR-MAPPED-ADDRESS attribute header
228///     0x00, 0x01, 0xa1, 0x47, // Address family (IPv4) and xor'd mapped port number
229///     0xe1, 0x12, 0xa6, 0x43, // Xor'd mapped IPv4 address
230///     0x00, 0x08, 0x00, 0x14, // MESSAGE-INTEGRITY header
231///     0x2b, 0x91, 0xf5, 0x99, // }
232///     0xfd, 0x9e, 0x90, 0xc3, // }
233///     0x8c, 0x74, 0x89, 0xf9, // } HMAC-SHA1 fingerprint
234///     0x2a, 0xf9, 0xba, 0x53, // }
235///     0xf0, 0x6b, 0xe7, 0xd7, // }
236///     0x80, 0x28, 0x00, 0x04, // FINGERPRINT attribute header
237///     0xc0, 0x7d, 0x4c, 0x96, // Reserved for CRC32 fingerprint
238/// ];
239///
240/// // No message integrity SHA256 attribute in this buffer
241/// assert_eq!(get_input_text::<MessageIntegritySha256>(&sample_ipv4_response), None);
242///
243/// // Get input buffer to validate the MessageIntegrity attribute
244/// let input = get_input_text::<MessageIntegrity>(&sample_ipv4_response).unwrap();
245///
246/// // Input buffer includes the whole STUN message up to and including
247/// // the attribute preceding the MESSAGE-INTEGRITY attribute, and the length
248/// // is adjusted to point at the end of the MESSAGE-INTEGRITY value (52 bytes)
249/// assert_eq!(input, [
250///     0x01, 0x01, 0x00, 0x34, // Response type and message length (52 bytes)
251///     0x21, 0x12, 0xa4, 0x42, // Magic cookie
252///     0xb7, 0xe7, 0xa7, 0x01, // }
253///     0xbc, 0x34, 0xd6, 0x86, // }  Transaction ID
254///     0xfa, 0x87, 0xdf, 0xae, // }
255///     0x80, 0x22, 0x00, 0x0b, // SOFTWARE attribute header
256///     0x74, 0x65, 0x73, 0x74, // }
257///     0x20, 0x76, 0x65, 0x63, // }  UTF-8 server name (1 byte padding)
258///     0x74, 0x6f, 0x72, 0x20, // }
259///     0x00, 0x20, 0x00, 0x08, // XOR-MAPPED-ADDRESS attribute header
260///     0x00, 0x01, 0xa1, 0x47, // Address family (IPv4) and xor'd mapped port number
261///     0xe1, 0x12, 0xa6, 0x43, // Xor'd mapped IPv4 address
262/// ]);
263/// #
264/// #   Ok(())
265/// # }
266///```
267pub fn get_input_text<A>(buffer: &[u8]) -> Option<Vec<u8>>
268where
269    A: StunAttributeType,
270{
271    raw::get_input_text(buffer, A::get_type().as_u16()).ok()
272}