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