# STUN Coder
STUN Coder is a STUN protocol encoder and decoder for Rust.
The implementation is done according to [Session Traversal Utilities for NAT (STUN)](https://tools.ietf.org/html/rfc5389).
STUN extensions specified by the [Interactive Connectivity Establishment (ICE) protocol](https://tools.ietf.org/html/rfc8445#section-7.1) are also supported.
## Usage
An example of creating and encoding a STUN binding request:
```rust
// Create a request message
let message = stun_coder::StunMessage::create_request()
.add_attribute(stun_coder::StunAttribute::Software {
description: String::from("rust-stun-coder"),
})
.add_message_integrity()
.add_fingerprint();
// Encode it into bytes
let encoded_message = message.encode(Some("TEST_PASS")).unwrap();
println!("{:#X?}", encoded_message);
```
An example that decodes a sample request with Long-Term Authentication
```rust
// Encoded message
let msg_bytes: Vec = vec![
0x01, 0x01, 0x00, 0x48, 0x21, 0x12, 0xa4, 0x42, 0xb7, 0xe7, 0xa7, 0x01, 0xbc, 0x34,
0xd6, 0x86, 0xfa, 0x87, 0xdf, 0xae, 0x80, 0x22, 0x00, 0x0b, 0x74, 0x65, 0x73, 0x74,
0x20, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x00, 0x00, 0x20, 0x00, 0x14, 0x00, 0x02,
0xa1, 0x47, 0x01, 0x13, 0xa9, 0xfa, 0xa5, 0xd3, 0xf1, 0x79, 0xbc, 0x25, 0xf4, 0xb5,
0xbe, 0xd2, 0xb9, 0xd9, 0x00, 0x08, 0x00, 0x14, 0xBD, 0x3, 0x6D, 0x6A, 0x33, 0x17,
0x50, 0xDF, 0xE2, 0xED, 0xC5, 0x8E, 0x64, 0x34, 0x55, 0xCF, 0xF5, 0xC8, 0xE2, 0x64,
0x80, 0x28, 0x00, 0x04, 0x4F, 0x26, 0x02, 0x93,
];
// Integrity key used for verification
let integrity_key = Some("VOkJxbRl1RmTxUk/WvJxBt");
// Decode the message
let decoded_msg = stun_coder::StunMessage::decode(&msg_bytes, integrity_key).unwrap();
println!("{:?}", decoded_msg);
```
Example function that fetches the server reflexive address of all the local interfaces:
```rust
use std::io::{Error, ErrorKind};
use std::net::{SocketAddr, UdpSocket};
// Fetches mapped address of a local Socket
fn get_mapped_addr(binding_addr: SocketAddr) -> Result {
// Use Google's public STUN server
let stun_server = "stun.l.google.com:19302";
// Create a binding message
let binding_msg = stun_coder::StunMessage::create_request()
.add_attribute(stun_coder::StunAttribute::Software {
description: String::from("rust-stun-coder"),
}) // Add software attribute
.add_message_integrity() // Add message integrity attribute
.add_fingerprint(); // Add fingerprint attribute
let integrity_pass = "STUN_CODER_PASS"; // Integrity password to use
// Encode the binding_msg
let bytes = binding_msg.encode(Some(integrity_pass)).unwrap();
// Open a UDP socket
let udp_socket = UdpSocket::bind(binding_addr)?;
// Connect to the STUN server
udp_socket.connect(stun_server.clone())?;
// Send the binding request message
udp_socket.send(&bytes)?;
// Wait for a response
let mut response_buf = [0; 32];
udp_socket.recv(&mut response_buf)?;
// Decode the response
let stun_response =
stun_coder::StunMessage::decode(&response_buf, Some(integrity_pass)).unwrap();
// Find the XorMappedAddress attribute in the response
// It will contain our reflexive transport address
for attr in stun_response.get_attributes() {
if let stun_coder::StunAttribute::XorMappedAddress { socket_addr } = attr {
return Ok(*socket_addr);
}
}
Err(Error::new(
ErrorKind::InvalidData,
"No XorMappedAddress has been set in response.",
))
}
// Fetches server reflexive addresses of local interfaces
fn get_mapped_addresses() {
// Gather local interfaces
let local_interfaces = get_if_addrs::get_if_addrs().unwrap();
// Attempt to get a mapped address for each one of them
for interface in local_interfaces.iter() {
// Exclude loopback interfaces
if interface.is_loopback() {
continue;
}
// Form a local socket for the interface on port 2000
let host_addr = interface.ip();
let binding_addr = SocketAddr::new(host_addr, 2000);
match get_mapped_addr(binding_addr) {
Ok(mapped_socket_addr) => {
println!(
"Mapped host address {} to remote {}.",
binding_addr, mapped_socket_addr
);
}
Err(err) => {
println!(
"Failed to map host address {}. Error: {}.",
binding_addr, err
);
}
}
}
}
```
## Author
Ruben Harutyunyan ([@Vagr9K](https://twitter.com/Vagr9K))