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
//! 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.
//!
//! An example of creating and encoding a STUN binding request:
//!```
//! // 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
//! ```
//! // Encoded message
//! let msg_bytes: Vec<u8> = 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:
//!
//! ```
//! 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<SocketAddr, std::io::Error> {
//!     // 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
//!                 );
//!             }
//!         }
//!     }
//! }
//! ```

#![warn(missing_docs)]
#![warn(rustdoc::missing_doc_code_examples)]
#![allow(clippy::module_inception)]

#[macro_use]
extern crate num_derive;

mod attribute;
mod definitions;
mod header;
mod message;
mod utils;

#[cfg(test)]
mod tests;

pub use attribute::{AttributeDecodeError, AttributeEncodeError, StunAttribute};
pub use header::{
    HeaderDecodeError, HeaderEncodeError, StunHeader, StunMessageClass, StunMessageMethod,
};
pub use message::{MessageDecodeError, MessageEncodeError, StunMessage};
pub use utils::{check_for_stun_message_header, generate_transaction_id};