├── .github ├── actions-rs │ └── grcov.yml └── workflows │ ├── ci.yml │ └── coverage.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── binding_cli.rs └── binding_srv.rs └── src ├── channel.rs ├── client.rs ├── error.rs ├── lib.rs ├── message.rs ├── server.rs └── transport ├── mod.rs ├── tcp.rs └── udp.rs /.github/actions-rs/grcov.yml: -------------------------------------------------------------------------------- 1 | ignore-not-existing: true 2 | ignore: 3 | - "../*" 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md 2 | 3 | name: CI 4 | 5 | on: [push] 6 | 7 | jobs: 8 | check: 9 | name: Check 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | toolchain: [stable, beta, nightly] 14 | steps: 15 | - name: Checkout sources 16 | uses: actions/checkout@v1 17 | 18 | - name: Install ${{ matrix.toolchain }} toolchain 19 | uses: actions-rs/toolchain@v1 20 | with: 21 | profile: minimal 22 | toolchain: ${{ matrix.toolchain }} 23 | override: true 24 | 25 | - name: Run cargo check 26 | uses: actions-rs/cargo@v1 27 | with: 28 | command: check 29 | args: --all-features --all 30 | 31 | test: 32 | name: Test Suite 33 | runs-on: ubuntu-latest 34 | strategy: 35 | matrix: 36 | toolchain: [stable, beta, nightly] 37 | steps: 38 | - name: Checkout sources 39 | uses: actions/checkout@v1 40 | 41 | - name: Install ${{ matrix.toolchain }} toolchain 42 | uses: actions-rs/toolchain@v1 43 | with: 44 | profile: minimal 45 | toolchain: ${{ matrix.toolchain }} 46 | override: true 47 | 48 | - name: Run cargo test 49 | uses: actions-rs/cargo@v1 50 | with: 51 | command: test 52 | args: --all-features --all 53 | 54 | lints: 55 | name: Lints 56 | runs-on: ubuntu-latest 57 | strategy: 58 | matrix: 59 | toolchain: [stable, beta, nightly] 60 | steps: 61 | - name: Checkout sources 62 | uses: actions/checkout@v1 63 | 64 | - name: Install ${{ matrix.toolchain }} toolchain 65 | uses: actions-rs/toolchain@v1 66 | with: 67 | profile: minimal 68 | toolchain: ${{ matrix.toolchain }} 69 | override: true 70 | components: rustfmt, clippy 71 | 72 | - name: Run cargo fmt 73 | uses: actions-rs/cargo@v1 74 | with: 75 | command: fmt 76 | args: --all -- --check 77 | 78 | - name: Run cargo clippy 79 | uses: actions-rs/cargo@v1 80 | with: 81 | command: clippy 82 | args: --all-features --all -- -D warnings 83 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md 2 | 3 | name: Coverage 4 | 5 | on: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | grcov: 12 | name: Coverage 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v1 16 | 17 | - name: Install toolchain 18 | uses: actions-rs/toolchain@v1 19 | with: 20 | toolchain: nightly 21 | override: true 22 | 23 | - name: Execute tests 24 | uses: actions-rs/cargo@v1 25 | with: 26 | command: test 27 | args: --all --all-features 28 | env: 29 | CARGO_INCREMENTAL: 0 30 | RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off" 31 | 32 | - name: Gather coverage data 33 | id: coverage 34 | uses: actions-rs/grcov@v0.1 35 | 36 | - name: Coveralls upload 37 | uses: coverallsapp/github-action@master 38 | with: 39 | github-token: ${{ secrets.GITHUB_TOKEN }} 40 | parallel: true 41 | path-to-lcov: ${{ steps.coverage.outputs.report }} 42 | 43 | grcov_finalize: 44 | runs-on: ubuntu-latest 45 | needs: grcov 46 | steps: 47 | - name: Coveralls finalization 48 | uses: coverallsapp/github-action@master 49 | with: 50 | github-token: ${{ secrets.GITHUB_TOKEN }} 51 | parallel-finished: true 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustun" 3 | version = "0.5.0" 4 | authors = ["Takeru Ohta "] 5 | description = "A library for implementing STUN server and client asynchronously" 6 | homepage = "https://github.com/sile/rustun" 7 | repository = "https://github.com/sile/rustun" 8 | readme = "README.md" 9 | keywords = ["STUN", "asynchronous"] 10 | categories = ["asynchronous", "network-programming"] 11 | license = "MIT" 12 | edition = "2021" 13 | 14 | [badges] 15 | coveralls = {repository = "sile/rustun"} 16 | 17 | [dependencies] 18 | bytecodec = "0.4" 19 | factory = "0.1" 20 | fibers = "0.1" 21 | fibers_timeout_queue = "0.1" 22 | fibers_transport = "0.1.3" 23 | futures = "0.1" 24 | rand = "0.8" 25 | stun_codec = "0.3" 26 | trackable = "1" 27 | 28 | [dev-dependencies] 29 | clap = { version = "4", features = ["derive"] } 30 | fibers_global = "0.1" 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 Takeru Ohta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rustun 2 | ====== 3 | 4 | [![Crates.io: rustun](https://img.shields.io/crates/v/rustun.svg)](https://crates.io/crates/rustun) 5 | [![Documentation](https://docs.rs/rustun/badge.svg)](https://docs.rs/rustun) 6 | [![Actions Status](https://github.com/sile/rustun/workflows/CI/badge.svg)](https://github.com/sile/rustun/actions) 7 | [![Coverage Status](https://coveralls.io/repos/github/sile/rustun/badge.svg?branch=master)](https://coveralls.io/github/sile/rustun?branch=master) 8 | [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 9 | 10 | A Rust library for implementing STUN server and client asynchronously. 11 | 12 | [Documentation](https://docs.rs/rustun) 13 | 14 | The STUN protocol is defined in [RFC 5389](https://tools.ietf.org/html/rfc5389). 15 | 16 | Examples 17 | -------- 18 | 19 | An example that issues a `BINDING` request: 20 | 21 | 22 | ```rust 23 | use fibers_transport::UdpTransporter; 24 | use futures::Future; 25 | use rustun::channel::Channel; 26 | use rustun::client::Client; 27 | use rustun::message::Request; 28 | use rustun::server::{BindingHandler, UdpServer}; 29 | use rustun::transport::StunUdpTransporter; 30 | use rustun::Error; 31 | use stun_codec::{rfc5389, MessageDecoder, MessageEncoder}; 32 | 33 | let addr = "127.0.0.1:0".parse().unwrap(); 34 | 35 | // Starts UDP server 36 | let server = fibers_global::execute(UdpServer::start(fibers_global::handle(), addr, BindingHandler))?; 37 | let server_addr = server.local_addr(); 38 | fibers_global::spawn(server.map(|_| ()).map_err(|e| panic!("{}", e))); 39 | 40 | // Sents BINDING request 41 | let response = UdpTransporter::, MessageDecoder<_>>::bind(addr) 42 | .map_err(Error::from) 43 | .map(StunUdpTransporter::new) 44 | .map(Channel::new) 45 | .and_then(move |channel| { 46 | let client = Client::new(&fibers_global::handle(), channel); 47 | let request = Request::::new(rfc5389::methods::BINDING); 48 | client.call(server_addr, request) 49 | }); 50 | 51 | // Waits BINDING response 52 | let response = fibers_global::execute(response)?; 53 | assert!(response.is_ok()); 54 | ``` 55 | 56 | You can run example server and client which handle `Binding` method as follows: 57 | 58 | ```console 59 | // Starts the STUN server in a shell. 60 | $ cargo run --example binding_srv 61 | 62 | // Executes a STUN client in another shell. 63 | $ cargo run --example binding_cli -- 127.0.0.1 64 | Ok(SuccessResponse(Message { 65 | class: SuccessResponse, 66 | method: Method(1), 67 | transaction_id: TransactionId(0x344A403694972F5E53B69465), 68 | attributes: [Known { inner: XorMappedAddress(XorMappedAddress(V4(127.0.0.1:54754))), 69 | padding: Some(Padding([])) }] 70 | })) 71 | ``` 72 | -------------------------------------------------------------------------------- /examples/binding_cli.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate trackable; 3 | 4 | use clap::Parser; 5 | use fibers_transport::UdpTransporter; 6 | use futures::Future; 7 | use rustun::channel::Channel; 8 | use rustun::client::Client; 9 | use rustun::message::Request; 10 | use rustun::transport::StunUdpTransporter; 11 | use rustun::Error; 12 | use std::net::ToSocketAddrs; 13 | use stun_codec::rfc5389; 14 | use stun_codec::{MessageDecoder, MessageEncoder}; 15 | use trackable::error::Failed; 16 | use trackable::error::MainError; 17 | 18 | #[derive(Debug, Parser)] 19 | struct Args { 20 | host: String, 21 | 22 | #[clap(short, long, default_value_t = 3478)] 23 | port: usize, 24 | } 25 | 26 | fn main() -> Result<(), MainError> { 27 | let args = Args::parse(); 28 | let peer_addr = track_assert_some!( 29 | track_any_err!(format!("{}:{}", args.host, args.port).to_socket_addrs())? 30 | .filter(|x| x.is_ipv4()) 31 | .nth(0), 32 | Failed 33 | ); 34 | 35 | let local_addr = "0.0.0.0:0".parse().unwrap(); 36 | let response = UdpTransporter::, MessageDecoder<_>>::bind(local_addr) 37 | .map_err(Error::from) 38 | .map(StunUdpTransporter::new) 39 | .map(Channel::new) 40 | .and_then(move |channel| { 41 | let client = Client::new(&fibers_global::handle(), channel); 42 | let request = Request::::new(rfc5389::methods::BINDING); 43 | client.call(peer_addr, request) 44 | }); 45 | let response = track!(fibers_global::execute(response))?; 46 | println!("{:?}", response); 47 | Ok(()) 48 | } 49 | -------------------------------------------------------------------------------- /examples/binding_srv.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate trackable; 3 | 4 | use clap::Parser; 5 | use rustun::server::{BindingHandler, UdpServer}; 6 | use trackable::error::MainError; 7 | 8 | #[derive(Debug, Parser)] 9 | struct Args { 10 | #[clap(short, long, default_value_t = 3478)] 11 | port: u16, 12 | } 13 | 14 | fn main() -> Result<(), MainError> { 15 | let args = Args::parse(); 16 | let addr = track_any_err!(format!("0.0.0.0:{}", args.port).parse())?; 17 | 18 | let server = track!(fibers_global::execute(UdpServer::start( 19 | fibers_global::handle(), 20 | addr, 21 | BindingHandler 22 | )))?; 23 | track!(fibers_global::execute(server))?; 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /src/channel.rs: -------------------------------------------------------------------------------- 1 | //! Channel for sending and receiving STUN messages. 2 | use crate::message::{ 3 | ErrorResponse, Indication, InvalidMessage, MessageError, MessageErrorKind, MessageResult, 4 | Request, Response, SuccessResponse, 5 | }; 6 | use crate::transport::StunTransport; 7 | use crate::{Error, Result}; 8 | use fibers::sync::oneshot; 9 | use fibers_timeout_queue::TimeoutQueue; 10 | use futures::{Async, Future, Poll}; 11 | use std::collections::HashMap; 12 | use std::fmt; 13 | use std::time::Duration; 14 | use stun_codec::{Attribute, BrokenMessage, Message, MessageClass, Method, TransactionId}; 15 | use trackable::error::ErrorKindExt; 16 | 17 | type Reply = oneshot::Monitored, MessageError>; 18 | 19 | /// [`Channel`] builder. 20 | /// 21 | /// [`Channel`]: ./struct.Channel.html 22 | #[derive(Debug, Clone)] 23 | pub struct ChannelBuilder { 24 | request_timeout: Duration, 25 | } 26 | impl ChannelBuilder { 27 | /// The default value of `request_timeout`. 28 | /// 29 | /// > Reliability of STUN over TCP and TLS-over-TCP is handled by TCP 30 | /// > itself, and there are no retransmissions at the STUN protocol level. 31 | /// > However, for a request/response transaction, if the client has not 32 | /// > received a response by **Ti** seconds after it sent the SYN to establish 33 | /// > the connection, it considers the transaction to have timed out. **Ti** 34 | /// > SHOULD be configurable and SHOULD have a default of **39.5s**. 35 | /// > 36 | /// > [RFC 5389 -- 7.2.2. Sending over TCP or TLS-over-TCP] 37 | /// 38 | /// [RFC 5389 -- 7.2.2. Sending over TCP or TLS-over-TCP]: https://tools.ietf.org/html/rfc5389#section-7.2.2 39 | pub const DEFAULT_REQUEST_TIMEOUT_MS: u64 = 39_500; 40 | 41 | /// Makes a new `ChannelBuilder` instance with the default settings. 42 | pub fn new() -> Self { 43 | Self::default() 44 | } 45 | 46 | /// Sets the request timeout duration of the channel. 47 | /// 48 | /// The default value is `DEFAULT_REQUEST_TIMEOUT_MS`. 49 | pub fn request_timeout(&mut self, duration: Duration) -> &mut Self { 50 | self.request_timeout = duration; 51 | self 52 | } 53 | 54 | /// Makes a new `Channel` instance with the given settings. 55 | pub fn finish(&self, transporter: T) -> Channel 56 | where 57 | A: Attribute, 58 | T: StunTransport, 59 | { 60 | Channel { 61 | transporter, 62 | timeout_queue: TimeoutQueue::new(), 63 | request_timeout: self.request_timeout, 64 | transactions: HashMap::new(), 65 | } 66 | } 67 | } 68 | impl Default for ChannelBuilder { 69 | fn default() -> Self { 70 | ChannelBuilder { 71 | request_timeout: Duration::from_millis(Self::DEFAULT_REQUEST_TIMEOUT_MS), 72 | } 73 | } 74 | } 75 | 76 | /// Channel for sending and receiving STUN messages. 77 | pub struct Channel 78 | where 79 | A: Attribute, 80 | T: StunTransport, 81 | { 82 | transporter: T, 83 | timeout_queue: TimeoutQueue<(T::PeerAddr, TransactionId)>, 84 | request_timeout: Duration, 85 | transactions: HashMap<(T::PeerAddr, TransactionId), (Method, Reply)>, 86 | } 87 | impl fmt::Debug for Channel 88 | where 89 | A: Attribute, 90 | T: StunTransport, 91 | { 92 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 93 | write!(f, "Channel {{ .. }}") 94 | } 95 | } 96 | impl Channel 97 | where 98 | A: Attribute, 99 | T: StunTransport, 100 | { 101 | /// Makes a new `Channel` instance. 102 | /// 103 | /// This is equivalent to `ChannelBuilder::default().finish(transporter)`. 104 | pub fn new(transporter: T) -> Self { 105 | ChannelBuilder::default().finish(transporter) 106 | } 107 | 108 | /// Sends the given request message to the destination peer and 109 | /// returns a future that waits the corresponding response. 110 | #[allow(clippy::map_entry)] 111 | pub fn call( 112 | &mut self, 113 | peer: T::PeerAddr, 114 | request: Request, 115 | ) -> impl Future, Error = MessageError> { 116 | let id = request.transaction_id(); 117 | let method = request.method(); 118 | let (tx, rx) = oneshot::monitor(); 119 | if self.transactions.contains_key(&(peer.clone(), id)) { 120 | let e = MessageErrorKind::InvalidInput 121 | .cause(format!("Transaction ID conflicts: transaction_id={id:?}")); 122 | tx.exit(Err(track!(e).into())); 123 | } else if let Err(e) = track!(self 124 | .transporter 125 | .start_send(peer.clone(), request.into_message())) 126 | { 127 | tx.exit(Err(e.into())); 128 | } else { 129 | self.transactions.insert((peer.clone(), id), (method, tx)); 130 | self.timeout_queue.push((peer, id), self.request_timeout); 131 | } 132 | rx.map_err(MessageError::from) 133 | } 134 | 135 | /// Sends the given indication message to the destination peer. 136 | pub fn cast(&mut self, peer: T::PeerAddr, indication: Indication) -> MessageResult<()> { 137 | track!(self.transporter.start_send(peer, indication.into_message()))?; 138 | Ok(()) 139 | } 140 | 141 | /// Replies the given response message to the destination peer. 142 | pub fn reply(&mut self, peer: T::PeerAddr, response: Response) -> MessageResult<()> { 143 | let message = response 144 | .map(|m| m.into_message()) 145 | .unwrap_or_else(|m| m.into_message()); 146 | track!(self.transporter.start_send(peer, message))?; 147 | Ok(()) 148 | } 149 | 150 | /// Returns a reference to the transporter of the channel. 151 | pub fn transporter_ref(&self) -> &T { 152 | &self.transporter 153 | } 154 | 155 | /// Returns a mutable reference to the transporter of the channel. 156 | pub fn transporter_mut(&mut self) -> &mut T { 157 | &mut self.transporter 158 | } 159 | 160 | /// Returns the number of the outstanding request/response transactions in the channel. 161 | pub fn outstanding_transactions(&self) -> usize { 162 | self.transactions.len() 163 | } 164 | 165 | /// Polls the transmission of the all outstanding messages in the channel have been completed. 166 | /// 167 | /// If it has been completed, this will return `Ok(Async::Ready(()))`. 168 | pub fn poll_send(&mut self) -> Poll<(), Error> { 169 | Ok(track!(self.transporter.poll_send())?) 170 | } 171 | 172 | /// Polls reception of a message from a peer. 173 | #[allow(clippy::type_complexity)] 174 | pub fn poll_recv(&mut self) -> Poll)>, Error> { 175 | track!(self.handle_timeout())?; 176 | while let Async::Ready(item) = track!(self.transporter.poll_recv())? { 177 | if let Some((peer, message)) = item { 178 | if let Some(item) = track!(self.handle_message(peer, message))? { 179 | return Ok(Async::Ready(Some(item))); 180 | } 181 | } else { 182 | return Ok(Async::Ready(None)); 183 | } 184 | } 185 | Ok(Async::NotReady) 186 | } 187 | 188 | fn handle_timeout(&mut self) -> Result<()> { 189 | let transactions = &mut self.transactions; 190 | while let Some((peer, id)) = self 191 | .timeout_queue 192 | .filter_pop(|entry| transactions.contains_key(entry)) 193 | { 194 | if let Some((_, tx)) = transactions.remove(&(peer.clone(), id)) { 195 | let e = track!(MessageErrorKind::Timeout.error()); 196 | tx.exit(Err(e.into())); 197 | } 198 | track!(self.transporter.finish_transaction(&peer, id))?; 199 | } 200 | Ok(()) 201 | } 202 | 203 | fn handle_message( 204 | &mut self, 205 | peer: T::PeerAddr, 206 | message: std::result::Result, BrokenMessage>, 207 | ) -> Result)>> { 208 | let message = match message { 209 | Err(broken) => Some(self.handle_broken_message(&broken)), 210 | Ok(message) => match message.class() { 211 | MessageClass::Indication => Some(self.handle_indication(message)), 212 | MessageClass::Request => Some(self.handle_request(message)), 213 | MessageClass::SuccessResponse => { 214 | track!(self.handle_success_response(&peer, message))? 215 | } 216 | MessageClass::ErrorResponse => track!(self.handle_error_response(&peer, message))?, 217 | }, 218 | }; 219 | Ok(message.map(|m| (peer, m))) 220 | } 221 | 222 | fn handle_broken_message(&self, message: &BrokenMessage) -> RecvMessage { 223 | let bytecodec_error_kind = *message.error().kind(); 224 | let error = MessageErrorKind::MalformedAttribute.takes_over(message.error().clone()); 225 | RecvMessage::Invalid(InvalidMessage::new( 226 | message.method(), 227 | message.class(), 228 | message.transaction_id(), 229 | track!(error; bytecodec_error_kind).into(), 230 | )) 231 | } 232 | 233 | fn handle_indication(&self, message: Message) -> RecvMessage { 234 | let class = message.class(); 235 | let method = message.method(); 236 | let transaction_id = message.transaction_id(); 237 | match track!(Indication::from_message(message)) { 238 | Err(error) => { 239 | RecvMessage::Invalid(InvalidMessage::new(method, class, transaction_id, error)) 240 | } 241 | Ok(indication) => RecvMessage::Indication(indication), 242 | } 243 | } 244 | 245 | fn handle_request(&self, message: Message) -> RecvMessage { 246 | let class = message.class(); 247 | let method = message.method(); 248 | let transaction_id = message.transaction_id(); 249 | match track!(Request::from_message(message)) { 250 | Err(error) => { 251 | RecvMessage::Invalid(InvalidMessage::new(method, class, transaction_id, error)) 252 | } 253 | Ok(request) => RecvMessage::Request(request), 254 | } 255 | } 256 | 257 | fn handle_success_response( 258 | &mut self, 259 | peer: &T::PeerAddr, 260 | message: Message, 261 | ) -> Result>> { 262 | let class = message.class(); 263 | let method = message.method(); 264 | let transaction_id = message.transaction_id(); 265 | if let Some((method, tx)) = self.transactions.remove(&(peer.clone(), transaction_id)) { 266 | track!(self.transporter.finish_transaction(peer, transaction_id))?; 267 | let result = track!(SuccessResponse::from_message(message)) 268 | .and_then(|m| { 269 | track_assert_eq!(m.method(), method, MessageErrorKind::UnexpectedResponse); 270 | Ok(m) 271 | }) 272 | .map(Ok); 273 | tx.exit(result); 274 | Ok(None) 275 | } else { 276 | let error = 277 | track!(MessageErrorKind::UnexpectedResponse.cause("Unknown transaction ID")).into(); 278 | let message = 279 | RecvMessage::Invalid(InvalidMessage::new(method, class, transaction_id, error)); 280 | Ok(Some(message)) 281 | } 282 | } 283 | 284 | fn handle_error_response( 285 | &mut self, 286 | peer: &T::PeerAddr, 287 | message: Message, 288 | ) -> Result>> { 289 | let class = message.class(); 290 | let method = message.method(); 291 | let transaction_id = message.transaction_id(); 292 | if let Some((method, tx)) = self.transactions.remove(&(peer.clone(), transaction_id)) { 293 | track!(self.transporter.finish_transaction(peer, transaction_id))?; 294 | let result = track!(ErrorResponse::from_message(message)) 295 | .and_then(|m| { 296 | track_assert_eq!(m.method(), method, MessageErrorKind::UnexpectedResponse); 297 | Ok(m) 298 | }) 299 | .map(Err); 300 | tx.exit(result); 301 | Ok(None) 302 | } else { 303 | let error = 304 | track!(MessageErrorKind::UnexpectedResponse.cause("Unknown transaction ID")).into(); 305 | let message = 306 | RecvMessage::Invalid(InvalidMessage::new(method, class, transaction_id, error)); 307 | Ok(Some(message)) 308 | } 309 | } 310 | } 311 | 312 | /// Received message. 313 | /// 314 | /// Messages are received by calling `Channel::poll` method. 315 | #[allow(missing_docs)] 316 | #[derive(Debug)] 317 | pub enum RecvMessage { 318 | Request(Request), 319 | Indication(Indication), 320 | Invalid(InvalidMessage), 321 | } 322 | -------------------------------------------------------------------------------- /src/client.rs: -------------------------------------------------------------------------------- 1 | //! Basic STUN client. 2 | //! 3 | //! This module provides only a basic STUN client. 4 | //! If you want more elaborate one, please consider create your own client using [`Channel`] directly. 5 | //! 6 | //! [`Channel`]: ../channel/struct.Channel.html 7 | use crate::channel::Channel; 8 | use crate::message::{Indication, Request, Response}; 9 | use crate::transport::StunTransport; 10 | use crate::{Error, Result}; 11 | use fibers::sync::{mpsc, oneshot}; 12 | use fibers::Spawn; 13 | use futures::stream::Fuse; 14 | use futures::{Async, Future, IntoFuture, Poll, Stream}; 15 | use std::fmt; 16 | use std::marker::PhantomData; 17 | use stun_codec::Attribute; 18 | 19 | /// STUN client. 20 | #[derive(Debug, Clone)] 21 | pub struct Client 22 | where 23 | A: Attribute, 24 | T: StunTransport, 25 | { 26 | command_tx: mpsc::Sender>, 27 | _phantom: PhantomData, 28 | } 29 | impl Client 30 | where 31 | A: Attribute + Send + 'static, 32 | T: StunTransport + Send + 'static, 33 | T::PeerAddr: Send + 'static, 34 | { 35 | /// Makes a new `Client` instance that uses the given channel for sending/receiving messages. 36 | pub fn new(spawner: &S, channel: Channel) -> Self 37 | where 38 | S: Spawn + Clone + Send + 'static, 39 | { 40 | let (command_tx, command_rx) = mpsc::channel(); 41 | let channel_driver = ChannelDriver { 42 | spawner: spawner.clone(), 43 | channel: Ok(channel), 44 | command_rx: command_rx.fuse(), 45 | }; 46 | spawner.spawn(channel_driver); 47 | Client { 48 | command_tx, 49 | _phantom: PhantomData, 50 | } 51 | } 52 | 53 | /// Sends the given request message to the destination peer and 54 | /// returns a future that waits the corresponding response. 55 | pub fn call( 56 | &self, 57 | peer: T::PeerAddr, 58 | request: Request, 59 | ) -> impl Future, Error = Error> { 60 | let (tx, rx) = oneshot::monitor(); 61 | let command = Command::Call(peer, request, tx); 62 | track!(self.command_tx.send(command).map_err(Error::from)) 63 | .into_future() 64 | .and_then(move |()| rx.map_err(|e| track!(Error::from(e)))) 65 | } 66 | 67 | /// Sends the given indication message to the destination peer. 68 | /// 69 | /// # Errors 70 | /// 71 | /// If the channel being used by the client has dropped, 72 | /// this will return an `ErrorKind::Other` error. 73 | pub fn cast(&self, peer: T::PeerAddr, indication: Indication) -> Result<()> { 74 | let command = Command::Cast(peer, indication); 75 | track!(self.command_tx.send(command).map_err(Error::from)) 76 | } 77 | } 78 | 79 | enum Command { 80 | Call(P, Request, oneshot::Monitored, Error>), 81 | Cast(P, Indication), 82 | } 83 | impl fmt::Debug for Command { 84 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 85 | match self { 86 | Command::Call(..) => write!(f, "Call(..)"), 87 | Command::Cast(..) => write!(f, "Cast(..)"), 88 | } 89 | } 90 | } 91 | 92 | struct ChannelDriver 93 | where 94 | A: Attribute, 95 | T: StunTransport, 96 | { 97 | spawner: S, 98 | channel: Result>, 99 | command_rx: Fuse>>, 100 | } 101 | impl ChannelDriver 102 | where 103 | S: Spawn, 104 | A: Attribute + Send + 'static, 105 | T: StunTransport + Send + 'static, 106 | { 107 | fn handle_command(&mut self, command: Command) { 108 | match command { 109 | Command::Cast(peer, indication) => { 110 | if let Ok(channel) = self.channel.as_mut() { 111 | let _ = channel.cast(peer, indication); 112 | } 113 | } 114 | Command::Call(peer, request, reply) => match self.channel { 115 | Err(ref e) => { 116 | reply.exit(Err(track!(e.clone()))); 117 | } 118 | Ok(ref mut channel) => { 119 | let future = 120 | channel 121 | .call(peer, request) 122 | .map_err(Error::from) 123 | .then(move |result| { 124 | reply.exit(track!(result)); 125 | Ok(()) 126 | }); 127 | self.spawner.spawn(future); 128 | } 129 | }, 130 | } 131 | } 132 | } 133 | impl Future for ChannelDriver 134 | where 135 | S: Spawn, 136 | A: Attribute + Send + 'static, 137 | T: StunTransport + Send + 'static, 138 | { 139 | type Item = (); 140 | type Error = (); 141 | 142 | fn poll(&mut self) -> Poll { 143 | while let Async::Ready(command) = self.command_rx.poll().expect("never fails") { 144 | if let Some(command) = command { 145 | self.handle_command(command); 146 | } else { 147 | // All clients have dropped 148 | let outstanding_transactions = self 149 | .channel 150 | .as_mut() 151 | .ok() 152 | .map_or(0, |c| c.outstanding_transactions()); 153 | if outstanding_transactions == 0 { 154 | return Ok(Async::Ready(())); 155 | } else { 156 | break; 157 | } 158 | } 159 | } 160 | 161 | while self.channel.is_ok() { 162 | match track!(self.channel.as_mut().expect("never fails").poll_recv()) { 163 | Err(e) => { 164 | self.channel = Err(e); 165 | break; 166 | } 167 | Ok(Async::NotReady) => {} 168 | Ok(Async::Ready(_message)) => { 169 | // All received messages are ignored 170 | continue; 171 | } 172 | } 173 | if let Err(e) = track!(self.channel.as_mut().expect("never fails").poll_send()) { 174 | self.channel = Err(e); 175 | } 176 | break; 177 | } 178 | Ok(Async::NotReady) 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use fibers::sync::oneshot::MonitorError; 2 | use std::io; 3 | use std::sync::mpsc::SendError; 4 | use stun_codec::rfc5389::attributes::ErrorCode; 5 | use stun_codec::AttributeType; 6 | use trackable::error::{self, ErrorKindExt, TrackableError}; 7 | 8 | /// This crate specific `Error` type. 9 | #[derive(Debug, Clone, TrackableError)] 10 | pub struct Error(TrackableError); 11 | impl From> for Error { 12 | fn from(f: MonitorError) -> Self { 13 | f.unwrap_or_else(|| { 14 | ErrorKind::Other 15 | .cause("Monitor channel has disconnected") 16 | .into() 17 | }) 18 | } 19 | } 20 | impl From for Error { 21 | fn from(f: io::Error) -> Self { 22 | ErrorKind::Other.cause(f).into() 23 | } 24 | } 25 | impl From> for Error { 26 | fn from(_: SendError) -> Self { 27 | ErrorKind::Other.cause("Receiver has terminated").into() 28 | } 29 | } 30 | impl From for Error { 31 | fn from(f: bytecodec::Error) -> Self { 32 | let original_error_kind = *f.kind(); 33 | let kind = match original_error_kind { 34 | bytecodec::ErrorKind::InvalidInput => ErrorKind::InvalidInput, 35 | _ => ErrorKind::Other, 36 | }; 37 | track!(kind.takes_over(f); original_error_kind).into() 38 | } 39 | } 40 | impl From for Error { 41 | fn from(f: MessageError) -> Self { 42 | ErrorKind::InvalidMessage(f.kind().clone()) 43 | .takes_over(f) 44 | .into() 45 | } 46 | } 47 | impl From for Error { 48 | fn from(f: ErrorCode) -> Self { 49 | ErrorKind::Other 50 | .cause(format!("STUN error response: {f:?}")) 51 | .into() 52 | } 53 | } 54 | impl From for Error { 55 | fn from(f: fibers_transport::Error) -> Self { 56 | let original_error_kind = *f.kind(); 57 | let kind = match original_error_kind { 58 | fibers_transport::ErrorKind::InvalidInput => ErrorKind::InvalidInput, 59 | _ => ErrorKind::Other, 60 | }; 61 | track!(kind.takes_over(f); original_error_kind).into() 62 | } 63 | } 64 | 65 | /// Possible error kinds. 66 | #[derive(Debug, Clone)] 67 | pub enum ErrorKind { 68 | /// Input is invalid. 69 | InvalidInput, 70 | 71 | /// A message is invalid. 72 | /// 73 | /// This error does not affect the overall execution of a channel/client/server. 74 | InvalidMessage(MessageErrorKind), 75 | 76 | /// Other errors. 77 | Other, 78 | } 79 | impl error::ErrorKind for ErrorKind {} 80 | 81 | /// Message level error. 82 | #[derive(Debug, Clone, TrackableError)] 83 | #[trackable(error_kind = "MessageErrorKind")] 84 | pub struct MessageError(TrackableError); 85 | impl From> for MessageError { 86 | fn from(f: MonitorError) -> Self { 87 | f.unwrap_or_else(|| { 88 | MessageErrorKind::Other 89 | .cause("`Channel` instance has dropped") 90 | .into() 91 | }) 92 | } 93 | } 94 | impl From for MessageError { 95 | fn from(f: Error) -> Self { 96 | let original_error_kind = f.kind().clone(); 97 | track!(MessageErrorKind::Other.takes_over(f); original_error_kind).into() 98 | } 99 | } 100 | impl From for MessageError { 101 | fn from(f: fibers_transport::Error) -> Self { 102 | let original_error_kind = *f.kind(); 103 | let kind = match original_error_kind { 104 | fibers_transport::ErrorKind::InvalidInput => MessageErrorKind::InvalidInput, 105 | _ => MessageErrorKind::Other, 106 | }; 107 | track!(kind.takes_over(f); original_error_kind).into() 108 | } 109 | } 110 | 111 | /// Possible error kinds. 112 | #[derive(Debug, Clone)] 113 | pub enum MessageErrorKind { 114 | /// Unexpected response message. 115 | UnexpectedResponse, 116 | 117 | /// There are some malformed attributes in a message. 118 | MalformedAttribute, 119 | 120 | /// There are unknown comprehension-required attributes. 121 | /// 122 | /// If a server receives a message that contains unknown comprehension-required attributes, 123 | /// it should reply an `ErrorResponse` message that has the [`UnknownAttribute`] error code and 124 | /// an [`UnknownAttributes`] attribute. 125 | /// 126 | /// [`UnknownAttribute`]: https://docs.rs/stun_codec/0.1/stun_codec/rfc5389/errors/struct.UnknownAttribute.html 127 | /// [`UnknownAttributes`]: https://docs.rs/stun_codec/0.1/stun_codec/rfc5389/attributes/struct.UnknownAttributes.html 128 | UnknownAttributes(Vec), 129 | 130 | /// Input is invalid. 131 | InvalidInput, 132 | 133 | /// Operation timed out. 134 | Timeout, 135 | 136 | /// Other errors. 137 | Other, 138 | } 139 | impl error::ErrorKind for MessageErrorKind {} 140 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An asynchronous implementation of [STUN][RFC 5389] server and client. 2 | //! 3 | //! # Examples 4 | //! 5 | //! An example that issues a `BINDING` request: 6 | //! 7 | //! ``` 8 | //! # extern crate fibers_global; 9 | //! # extern crate fibers_transport; 10 | //! # extern crate futures; 11 | //! # extern crate rustun; 12 | //! # extern crate stun_codec; 13 | //! # extern crate trackable; 14 | //! use fibers_transport::UdpTransporter; 15 | //! use futures::Future; 16 | //! use rustun::channel::Channel; 17 | //! use rustun::client::Client; 18 | //! use rustun::message::Request; 19 | //! use rustun::server::{BindingHandler, UdpServer}; 20 | //! use rustun::transport::StunUdpTransporter; 21 | //! use rustun::Error; 22 | //! use stun_codec::{rfc5389, MessageDecoder, MessageEncoder}; 23 | //! 24 | //! # fn main() -> Result<(), trackable::error::MainError> { 25 | //! let addr = "127.0.0.1:0".parse().unwrap(); 26 | //! 27 | //! // Starts UDP server 28 | //! let server = fibers_global::execute(UdpServer::start(fibers_global::handle(), addr, BindingHandler))?; 29 | //! let server_addr = server.local_addr(); 30 | //! fibers_global::spawn(server.map(|_| ()).map_err(|e| panic!("{}", e))); 31 | //! 32 | //! // Sents BINDING request 33 | //! let response = UdpTransporter::, MessageDecoder<_>>::bind(addr) 34 | //! .map_err(Error::from) 35 | //! .map(StunUdpTransporter::new) 36 | //! .map(Channel::new) 37 | //! .and_then(move |channel| { 38 | //! let client = Client::new(&fibers_global::handle(), channel); 39 | //! let request = Request::::new(rfc5389::methods::BINDING); 40 | //! client.call(server_addr, request) 41 | //! }); 42 | //! 43 | //! // Waits BINDING response 44 | //! let response = fibers_global::execute(response)?; 45 | //! assert!(response.is_ok()); 46 | //! # Ok(()) 47 | //! # } 48 | //! ``` 49 | //! 50 | //! You can run example server and client that handle `BINDING` method as follows: 51 | //! 52 | //! ```console 53 | //! // Starts the STUN server in a shell. 54 | //! $ cargo run --example binding_srv 55 | //! 56 | //! // Executes a STUN client in another shell. 57 | //! $ cargo run --example binding_cli -- 127.0.0.1 58 | //! Ok(SuccessResponse(Message { 59 | //! class: SuccessResponse, 60 | //! method: Method(1), 61 | //! transaction_id: TransactionId(0x344A403694972F5E53B69465), 62 | //! attributes: [Known { inner: XorMappedAddress(XorMappedAddress(V4(127.0.0.1:54754))), 63 | //! padding: Some(Padding([])) }] 64 | //! })) 65 | //! ``` 66 | //! 67 | //! # References 68 | //! 69 | //! - [RFC 5389 - Session Traversal Utilities for NAT (STUN)][RFC 5389] 70 | //! 71 | //! [RFC 5389]: https://tools.ietf.org/html/rfc5389 72 | #![warn(missing_docs)] 73 | #[macro_use] 74 | extern crate trackable; 75 | 76 | pub use error::{Error, ErrorKind}; 77 | 78 | pub mod channel; 79 | pub mod client; 80 | pub mod message; 81 | pub mod server; 82 | pub mod transport; 83 | 84 | mod error; 85 | 86 | /// A specialized `Result` type for this crate. 87 | pub type Result = std::result::Result; 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use crate::channel::Channel; 92 | use crate::client::Client; 93 | use crate::message::Request; 94 | use crate::server::{BindingHandler, TcpServer, UdpServer}; 95 | use crate::transport::{StunTcpTransporter, StunUdpTransporter}; 96 | use crate::Error; 97 | use factory::DefaultFactory; 98 | use fibers_global; 99 | use fibers_transport::{TcpTransporter, UdpTransporter}; 100 | use futures::Future; 101 | use std::thread; 102 | use std::time::Duration; 103 | use stun_codec::rfc5389; 104 | use stun_codec::{MessageDecoder, MessageEncoder}; 105 | use trackable::error::MainError; 106 | 107 | #[test] 108 | fn basic_udp_test() -> Result<(), MainError> { 109 | let server = fibers_global::execute(UdpServer::start( 110 | fibers_global::handle(), 111 | "127.0.0.1:0".parse().unwrap(), 112 | BindingHandler, 113 | ))?; 114 | let server_addr = server.local_addr(); 115 | fibers_global::spawn(server.map(|_| ()).map_err(|e| panic!("{}", e))); 116 | 117 | let client_addr = "127.0.0.1:0".parse().unwrap(); 118 | let response = UdpTransporter::, MessageDecoder<_>>::bind(client_addr) 119 | .map_err(Error::from) 120 | .map(StunUdpTransporter::new) 121 | .map(Channel::new) 122 | .and_then(move |channel| { 123 | let client = Client::new(&fibers_global::handle(), channel); 124 | let request = Request::::new(rfc5389::methods::BINDING); 125 | client.call(server_addr, request) 126 | }); 127 | let response = track!(fibers_global::execute(response))?; 128 | assert!(response.is_ok()); 129 | 130 | Ok(()) 131 | } 132 | 133 | #[test] 134 | fn basic_tcp_test() -> Result<(), MainError> { 135 | let server = fibers_global::execute(TcpServer::start( 136 | fibers_global::handle(), 137 | "127.0.0.1:0".parse().unwrap(), 138 | DefaultFactory::::new(), 139 | ))?; 140 | let server_addr = server.local_addr(); 141 | 142 | fibers_global::spawn(server.map(|_| ()).map_err(|e| panic!("{}", e))); 143 | thread::sleep(Duration::from_millis(50)); 144 | 145 | let response = TcpTransporter::, MessageDecoder<_>>::connect(server_addr) 146 | .map_err(Error::from) 147 | .map(StunTcpTransporter::new) 148 | .map(Channel::new) 149 | .and_then(move |channel| { 150 | let client = Client::new(&fibers_global::handle(), channel); 151 | let request = Request::::new(rfc5389::methods::BINDING); 152 | client.call((), request) 153 | }); 154 | let response = track!(fibers_global::execute(response))?; 155 | assert!(response.is_ok()); 156 | 157 | Ok(()) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/message.rs: -------------------------------------------------------------------------------- 1 | //! STUN messages. 2 | //! 3 | //! > STUN is a client-server protocol. It supports two types of 4 | //! > transactions. One is a **request/response** transaction in which a 5 | //! > client sends a **request** to a server, and the server returns a 6 | //! > **response**. The second is an **indication** transaction in which either 7 | //! > agent -- client or server -- sends an indication that generates no 8 | //! > response. Both types of transactions include a transaction ID, which 9 | //! > is a randomly selected 96-bit number. For **request/response** 10 | //! > transactions, this transaction ID allows the client to associate the 11 | //! > response with the request that generated it; for indications, the 12 | //! > transaction ID serves as a debugging aid. 13 | //! > 14 | //! > All STUN messages start with a fixed header that includes a method, a 15 | //! > class, and the transaction ID. The method indicates which of the 16 | //! > various requests or indications this is; this specification defines 17 | //! > just one method, Binding, but other methods are expected to be 18 | //! > defined in other documents. The class indicates whether this is a 19 | //! > **request**, a **success response**, an **error response**, or an **indication**. 20 | //! > Following the fixed header comes zero or more attributes, which are 21 | //! > Type-Length-Value extensions that convey additional information for 22 | //! > the specific message. 23 | //! > 24 | //! > [RFC 5389 -- 3. Overview of Operation] 25 | //! 26 | //! [RFC 5389 -- 3. Overview of Operation]: https://tools.ietf.org/html/rfc5389#section-3 27 | use stun_codec::convert::TryAsRef; 28 | use stun_codec::rfc5389::attributes::ErrorCode; 29 | use stun_codec::{Attribute, Message, MessageClass, Method, TransactionId}; 30 | 31 | pub use crate::error::{MessageError, MessageErrorKind}; 32 | 33 | /// A specialized `Result` type for message-level operations. 34 | pub type MessageResult = Result; 35 | 36 | /// Invalid message. 37 | #[derive(Debug, Clone)] 38 | pub struct InvalidMessage { 39 | method: Method, 40 | class: MessageClass, 41 | transaction_id: TransactionId, 42 | error: MessageError, 43 | } 44 | impl InvalidMessage { 45 | /// Returns the method of the message. 46 | pub fn method(&self) -> Method { 47 | self.method 48 | } 49 | 50 | /// Returns the class of the message. 51 | pub fn class(&self) -> MessageClass { 52 | self.class 53 | } 54 | 55 | /// Returns the transaction ID of the message. 56 | pub fn transaction_id(&self) -> TransactionId { 57 | self.transaction_id 58 | } 59 | 60 | /// Returns a reference to the error object that describes why the message is invalid. 61 | pub fn error(&self) -> &MessageError { 62 | &self.error 63 | } 64 | 65 | pub(crate) fn new( 66 | method: Method, 67 | class: MessageClass, 68 | transaction_id: TransactionId, 69 | error: MessageError, 70 | ) -> Self { 71 | InvalidMessage { 72 | method, 73 | class, 74 | transaction_id, 75 | error, 76 | } 77 | } 78 | } 79 | 80 | /// Response message. 81 | pub type Response = std::result::Result, ErrorResponse>; 82 | 83 | /// Request message. 84 | #[derive(Debug, Clone)] 85 | pub struct Request(Message); 86 | impl Request { 87 | /// Makes a new request message. 88 | pub fn new(method: Method) -> Self { 89 | Request(Message::new( 90 | MessageClass::Request, 91 | method, 92 | TransactionId::new(rand::random()), 93 | )) 94 | } 95 | 96 | /// Converts `Message` to `Request`. 97 | /// 98 | /// # Errors 99 | /// 100 | /// If the class of the given message is not `MessageClass::Request`, 101 | /// this function will return a `MessageErrorKind::InvalidInput` error. 102 | /// 103 | /// And if the message contains some unknown comprehension-required attributes, 104 | /// this function will return a `MessageErrorKind::UnknownAttributes` error. 105 | pub fn from_message(message: Message) -> MessageResult { 106 | track_assert_eq!( 107 | message.class(), 108 | MessageClass::Request, 109 | MessageErrorKind::InvalidInput 110 | ); 111 | track!(check_unknown_attributes(&message))?; 112 | Ok(Request(message)) 113 | } 114 | 115 | /// Returns the method of the message. 116 | pub fn method(&self) -> Method { 117 | self.0.method() 118 | } 119 | 120 | /// Returns the transaction ID of the message. 121 | pub fn transaction_id(&self) -> TransactionId { 122 | self.0.transaction_id() 123 | } 124 | 125 | /// Returns a reference to the first occurance of `T` attribute in the attributes of the message. 126 | /// 127 | /// If there is no such attribute, this method will return `None`. 128 | pub fn get_attribute(&self) -> Option<&T> 129 | where 130 | T: Attribute, 131 | A: TryAsRef, 132 | { 133 | self.0.get_attribute() 134 | } 135 | 136 | /// Returns an iterator that iterates over the known attributes in the message. 137 | pub fn attributes(&self) -> impl Iterator { 138 | self.0.attributes() 139 | } 140 | 141 | /// Adds the given attribute to the tail of the attributes in the message. 142 | pub fn add_attribute(&mut self, attribute: A) { 143 | self.0.add_attribute(attribute); 144 | } 145 | 146 | /// Takes ownership of this instance, and returns the internal message. 147 | pub fn into_message(self) -> Message { 148 | self.0 149 | } 150 | } 151 | impl AsRef> for Request { 152 | fn as_ref(&self) -> &Message { 153 | &self.0 154 | } 155 | } 156 | impl AsMut> for Request { 157 | fn as_mut(&mut self) -> &mut Message { 158 | &mut self.0 159 | } 160 | } 161 | 162 | /// Indication message. 163 | #[derive(Debug, Clone)] 164 | pub struct Indication(Message); 165 | impl Indication { 166 | /// Makes a new indication message. 167 | pub fn new(method: Method) -> Self { 168 | Indication(Message::new( 169 | MessageClass::Indication, 170 | method, 171 | TransactionId::new(rand::random()), 172 | )) 173 | } 174 | 175 | /// Converts `Message` to `Indication`. 176 | /// 177 | /// # Errors 178 | /// 179 | /// If the class of the given message is not `MessageClass::Indication`, 180 | /// this function will return a `MessageErrorKind::InvalidInput` error. 181 | /// 182 | /// And if the message contains some unknown comprehension-required attributes, 183 | /// this function will return a `MessageErrorKind::UnknownAttributes` error. 184 | pub fn from_message(message: Message) -> MessageResult { 185 | track_assert_eq!( 186 | message.class(), 187 | MessageClass::Indication, 188 | MessageErrorKind::InvalidInput 189 | ); 190 | track!(check_unknown_attributes(&message))?; 191 | Ok(Indication(message)) 192 | } 193 | 194 | /// Returns the method of the message. 195 | pub fn method(&self) -> Method { 196 | self.0.method() 197 | } 198 | 199 | /// Returns the transaction ID of the message. 200 | pub fn transaction_id(&self) -> TransactionId { 201 | self.0.transaction_id() 202 | } 203 | 204 | /// Returns a reference to the first occurance of `T` attribute in the attributes of the message. 205 | /// 206 | /// If there is no such attribute, this method will return `None`. 207 | pub fn get_attribute(&self) -> Option<&T> 208 | where 209 | T: Attribute, 210 | A: TryAsRef, 211 | { 212 | self.0.get_attribute() 213 | } 214 | 215 | /// Returns an iterator that iterates over the known attributes in the message. 216 | pub fn attributes(&self) -> impl Iterator { 217 | self.0.attributes() 218 | } 219 | 220 | /// Adds the given attribute to the tail of the attributes in the message. 221 | pub fn add_attribute(&mut self, attribute: A) { 222 | self.0.add_attribute(attribute); 223 | } 224 | 225 | /// Takes ownership of this instance, and returns the internal message. 226 | pub fn into_message(self) -> Message { 227 | self.0 228 | } 229 | } 230 | impl AsRef> for Indication { 231 | fn as_ref(&self) -> &Message { 232 | &self.0 233 | } 234 | } 235 | impl AsMut> for Indication { 236 | fn as_mut(&mut self) -> &mut Message { 237 | &mut self.0 238 | } 239 | } 240 | 241 | /// Success response message. 242 | #[derive(Debug, Clone)] 243 | pub struct SuccessResponse(Message); 244 | impl SuccessResponse { 245 | /// Makes a new `SuccessResponse` instance for the success response to the given request. 246 | pub fn new(request: &Request) -> Self { 247 | SuccessResponse(Message::new( 248 | MessageClass::SuccessResponse, 249 | request.method(), 250 | request.transaction_id(), 251 | )) 252 | } 253 | 254 | /// Converts `Message` to `SuccessResponse`. 255 | /// 256 | /// # Errors 257 | /// 258 | /// If the class of the given message is not `MessageClass::SuccessResponse`, 259 | /// this function will return a `MessageErrorKind::InvalidInput` error. 260 | /// 261 | /// And if the message contains some unknown comprehension-required attributes, 262 | /// this function will return a `MessageErrorKind::UnknownAttributes` error. 263 | pub fn from_message(message: Message) -> MessageResult { 264 | track_assert_eq!( 265 | message.class(), 266 | MessageClass::SuccessResponse, 267 | MessageErrorKind::InvalidInput 268 | ); 269 | track!(check_unknown_attributes(&message))?; 270 | Ok(SuccessResponse(message)) 271 | } 272 | 273 | /// Returns the method of the message. 274 | pub fn method(&self) -> Method { 275 | self.0.method() 276 | } 277 | 278 | /// Returns the transaction ID of the message. 279 | pub fn transaction_id(&self) -> TransactionId { 280 | self.0.transaction_id() 281 | } 282 | 283 | /// Returns a reference to the first occurance of `T` attribute in the attributes of the message. 284 | /// 285 | /// If there is no such attribute, this method will return `None`. 286 | pub fn get_attribute(&self) -> Option<&T> 287 | where 288 | T: Attribute, 289 | A: TryAsRef, 290 | { 291 | self.0.get_attribute() 292 | } 293 | 294 | /// Returns an iterator that iterates over the known attributes in the message. 295 | pub fn attributes(&self) -> impl Iterator { 296 | self.0.attributes() 297 | } 298 | 299 | /// Adds the given attribute to the tail of the attributes in the message. 300 | pub fn add_attribute(&mut self, attribute: A) { 301 | self.0.add_attribute(attribute); 302 | } 303 | 304 | /// Takes ownership of this instance, and returns the internal message. 305 | pub fn into_message(self) -> Message { 306 | self.0 307 | } 308 | } 309 | impl AsRef> for SuccessResponse { 310 | fn as_ref(&self) -> &Message { 311 | &self.0 312 | } 313 | } 314 | impl AsMut> for SuccessResponse { 315 | fn as_mut(&mut self) -> &mut Message { 316 | &mut self.0 317 | } 318 | } 319 | 320 | /// Error response message. 321 | #[derive(Debug, Clone)] 322 | pub struct ErrorResponse(Message); 323 | impl ErrorResponse { 324 | /// Makes a new `ErrorResponse` instance for the error response to the given request. 325 | pub fn new(request: &Request, error: ErrorCode) -> Self 326 | where 327 | A: From, 328 | { 329 | let mut message = Message::new( 330 | MessageClass::ErrorResponse, 331 | request.method(), 332 | request.transaction_id(), 333 | ); 334 | message.add_attribute(error); 335 | ErrorResponse(message) 336 | } 337 | 338 | /// Converts `Message` to `ErrorResponse`. 339 | /// 340 | /// # Errors 341 | /// 342 | /// If the class of the given message is not `MessageClass::ErrorResponse` or 343 | /// the message does not contains an `ErrorCode` attribute, 344 | /// this function will return a `ErrorKind::InvalidInput` error. 345 | /// 346 | /// And if the message contains some unknown comprehension-required attributes, 347 | /// this function will return a `ErrorKind::UnknownAttributes` error. 348 | pub fn from_message(message: Message) -> MessageResult { 349 | track_assert_eq!( 350 | message.class(), 351 | MessageClass::ErrorResponse, 352 | MessageErrorKind::InvalidInput 353 | ); 354 | track!(check_unknown_attributes(&message))?; 355 | 356 | let contains_error_code = message 357 | .attributes() 358 | .map(|a| a.get_type()) 359 | .chain(message.unknown_attributes().map(|a| a.get_type())) 360 | .any(|t| t.as_u16() == ErrorCode::CODEPOINT); 361 | track_assert!(contains_error_code, MessageErrorKind::InvalidInput); 362 | Ok(ErrorResponse(message)) 363 | } 364 | 365 | /// Returns the method of the message. 366 | pub fn method(&self) -> Method { 367 | self.0.method() 368 | } 369 | 370 | /// Returns the transaction ID of the message. 371 | pub fn transaction_id(&self) -> TransactionId { 372 | self.0.transaction_id() 373 | } 374 | 375 | /// Returns a reference to the first occurance of `T` attribute in the attributes of the message. 376 | /// 377 | /// If there is no such attribute, this method will return `None`. 378 | pub fn get_attribute(&self) -> Option<&T> 379 | where 380 | T: Attribute, 381 | A: TryAsRef, 382 | { 383 | self.0.get_attribute() 384 | } 385 | 386 | /// Returns an iterator that iterates over the known attributes in the message. 387 | pub fn attributes(&self) -> impl Iterator { 388 | self.0.attributes() 389 | } 390 | 391 | /// Adds the given attribute to the tail of the attributes in the message. 392 | pub fn add_attribute(&mut self, attribute: A) { 393 | self.0.add_attribute(attribute); 394 | } 395 | 396 | /// Takes ownership of this instance, and returns the internal message. 397 | pub fn into_message(self) -> Message { 398 | self.0 399 | } 400 | } 401 | impl AsRef> for ErrorResponse { 402 | fn as_ref(&self) -> &Message { 403 | &self.0 404 | } 405 | } 406 | impl AsMut> for ErrorResponse { 407 | fn as_mut(&mut self) -> &mut Message { 408 | &mut self.0 409 | } 410 | } 411 | 412 | fn check_unknown_attributes(message: &Message) -> MessageResult<()> { 413 | let required_unknowns = message 414 | .unknown_attributes() 415 | .filter_map(|a| { 416 | if a.get_type().is_comprehension_required() { 417 | Some(a.get_type()) 418 | } else { 419 | None 420 | } 421 | }) 422 | .collect::>(); 423 | track_assert!( 424 | required_unknowns.is_empty(), 425 | MessageErrorKind::UnknownAttributes(required_unknowns) 426 | ); 427 | Ok(()) 428 | } 429 | -------------------------------------------------------------------------------- /src/server.rs: -------------------------------------------------------------------------------- 1 | //! Basic STUN servers. 2 | //! 3 | //! This module provides only a basic STUN servers. 4 | //! If you want more elaborate one, please consider create your own server using [`Channel`] directly. 5 | //! 6 | //! [`Channel`]: ../channel/struct.Channel.html 7 | use crate::channel::{Channel, RecvMessage}; 8 | use crate::message::{ 9 | ErrorResponse, Indication, InvalidMessage, Request, Response, SuccessResponse, 10 | }; 11 | use crate::transport::{StunTcpTransporter, StunTransport, StunUdpTransporter}; 12 | use crate::{Error, ErrorKind, Result}; 13 | use bytecodec::marker::Never; 14 | use factory::DefaultFactory; 15 | use factory::Factory; 16 | use fibers::sync::mpsc; 17 | use fibers::{BoxSpawn, Spawn}; 18 | use fibers_transport::{FixedPeerTransporter, TcpTransport, UdpTransport}; 19 | use futures::{Async, Future, Poll, Stream}; 20 | use std::fmt; 21 | use std::net::SocketAddr; 22 | use stun_codec::rfc5389; 23 | use stun_codec::{Attribute, MessageDecoder, MessageEncoder}; 24 | 25 | /// The default TCP and UDP port for STUN. 26 | pub const DEFAULT_PORT: u16 = 3478; 27 | 28 | /// The default TLS port for STUN. 29 | pub const DEFAULT_TLS_PORT: u16 = 5349; 30 | 31 | type UdpTransporter = fibers_transport::UdpTransporter, MessageDecoder>; 32 | 33 | /// UDP based STUN server. 34 | #[derive(Debug)] 35 | #[must_use = "future do nothing unless polled"] 36 | pub struct UdpServer { 37 | driver: HandlerDriver>>, 38 | } 39 | impl UdpServer { 40 | /// Starts the server. 41 | pub fn start( 42 | spawner: S, 43 | bind_addr: SocketAddr, 44 | handler: H, 45 | ) -> impl Future 46 | where 47 | S: Spawn + Send + 'static, 48 | { 49 | UdpTransporter::bind(bind_addr) 50 | .map_err(|e| track!(Error::from(e))) 51 | .map(move |transporter| { 52 | let channel = Channel::new(StunUdpTransporter::new(transporter)); 53 | let driver = HandlerDriver::new(spawner.boxed(), handler, channel, true); 54 | UdpServer { driver } 55 | }) 56 | } 57 | 58 | /// Returns the address to which the server is bound. 59 | pub fn local_addr(&self) -> SocketAddr { 60 | self.driver 61 | .channel 62 | .transporter_ref() 63 | .inner_ref() 64 | .local_addr() 65 | } 66 | } 67 | impl Future for UdpServer { 68 | type Item = Never; 69 | type Error = Error; 70 | 71 | fn poll(&mut self) -> Poll { 72 | if let Async::Ready(()) = track!(self.driver.poll())? { 73 | track_panic!(ErrorKind::Other, "STUN UDP server unexpectedly terminated"); 74 | } 75 | Ok(Async::NotReady) 76 | } 77 | } 78 | 79 | type TcpListener = fibers_transport::TcpListener< 80 | DefaultFactory>, 81 | DefaultFactory>, 82 | >; 83 | 84 | /// TCP based STUN server. 85 | #[must_use = "future do nothing unless polled"] 86 | pub struct TcpServer 87 | where 88 | H: Factory, 89 | H::Item: HandleMessage, 90 | { 91 | spawner: S, 92 | handler_factory: H, 93 | listener: TcpListener<::Attribute>, 94 | } 95 | impl TcpServer 96 | where 97 | S: Spawn + Clone + Send + 'static, 98 | H: Factory, 99 | H::Item: HandleMessage, 100 | { 101 | /// Starts the server. 102 | pub fn start( 103 | spawner: S, 104 | bind_addr: SocketAddr, 105 | handler_factory: H, 106 | ) -> impl Future { 107 | TcpListener::listen(bind_addr) 108 | .map_err(|e| track!(Error::from(e))) 109 | .map(move |listener| TcpServer { 110 | spawner, 111 | handler_factory, 112 | listener, 113 | }) 114 | } 115 | 116 | /// Returns the address to which the server is bound. 117 | pub fn local_addr(&self) -> SocketAddr { 118 | self.listener.local_addr() 119 | } 120 | } 121 | impl Future for TcpServer 122 | where 123 | S: Spawn + Clone + Send + 'static, 124 | H: Factory, 125 | H::Item: HandleMessage + Send + 'static, 126 | <::Attribute as Attribute>::Decoder: Send + 'static, 127 | <::Attribute as Attribute>::Encoder: Send + 'static, 128 | { 129 | type Item = Never; 130 | type Error = Error; 131 | 132 | fn poll(&mut self) -> Poll { 133 | while let Async::Ready(transporter) = track!(self.listener.poll())? { 134 | if let Some(transporter) = transporter { 135 | let peer_addr = transporter.peer_addr(); 136 | let transporter = 137 | FixedPeerTransporter::new(peer_addr, (), StunTcpTransporter::new(transporter)); 138 | let channel = Channel::new(transporter); 139 | let handler = self.handler_factory.create(); 140 | let future = 141 | HandlerDriver::new(self.spawner.clone().boxed(), handler, channel, false); 142 | self.spawner.spawn(future.map_err(|_| ())); 143 | } else { 144 | track_panic!(ErrorKind::Other, "STUN TCP server unexpectedly terminated"); 145 | } 146 | } 147 | Ok(Async::NotReady) 148 | } 149 | } 150 | impl fmt::Debug for TcpServer 151 | where 152 | H: Factory, 153 | H::Item: HandleMessage, 154 | { 155 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 156 | write!(f, "TcpServer {{ .. }}") 157 | } 158 | } 159 | 160 | /// Action instructed by an operation of a message handler. 161 | pub enum Action { 162 | /// Replies an response to the client immediately. 163 | Reply(T), 164 | 165 | /// Replies an response to the client in the future. 166 | FutureReply(Box + Send + 'static>), 167 | 168 | /// Does not reply to the client. 169 | NoReply, 170 | 171 | /// Does not reply to the client, but does something for handling the incoming message. 172 | FutureNoReply(Box + Send + 'static>), 173 | } 174 | impl fmt::Debug for Action { 175 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 176 | match self { 177 | Action::Reply(t) => write!(f, "Reply({t:?})"), 178 | Action::FutureReply(_) => write!(f, "FutureReply(_)"), 179 | Action::NoReply => write!(f, "NoReply"), 180 | Action::FutureNoReply(_) => write!(f, "FutureNoReply(_)"), 181 | } 182 | } 183 | } 184 | 185 | /// This trait allows for handling messages sent by clients. 186 | #[allow(unused_variables)] 187 | pub trait HandleMessage { 188 | /// The attributes that the handler can recognize. 189 | type Attribute: Attribute + Send + 'static; 190 | 191 | /// Handles a request message. 192 | /// 193 | /// The default implementation always returns `Action::NoReply`. 194 | fn handle_call( 195 | &mut self, 196 | peer: SocketAddr, 197 | request: Request, 198 | ) -> Action> { 199 | Action::NoReply 200 | } 201 | 202 | /// Handles an indication message. 203 | /// 204 | /// The default implementation always returns `Action::NoReply`. 205 | fn handle_cast( 206 | &mut self, 207 | peer: SocketAddr, 208 | indication: Indication, 209 | ) -> Action { 210 | Action::NoReply 211 | } 212 | 213 | /// Handles an invalid incoming message. 214 | /// 215 | /// Note that this method should not return `Action::Reply(_)` or `Action::FutureReply(_)` 216 | /// if the class of `message` is not `MessageClass::Request`. 217 | /// 218 | /// The default implementation always returns `Action::NoReply`. 219 | fn handle_invalid_message( 220 | &mut self, 221 | peer: SocketAddr, 222 | message: InvalidMessage, 223 | ) -> Action> { 224 | Action::NoReply 225 | } 226 | 227 | /// Handles an error before the channel drops by the error. 228 | /// 229 | /// The default implementation does nothing. 230 | fn handle_channel_error(&mut self, error: &Error) {} 231 | } 232 | 233 | #[derive(Debug)] 234 | struct HandlerDriver 235 | where 236 | H: HandleMessage, 237 | T: StunTransport, 238 | { 239 | spawner: BoxSpawn, 240 | handler: H, 241 | channel: Channel, 242 | response_tx: mpsc::Sender<(SocketAddr, Response)>, 243 | response_rx: mpsc::Receiver<(SocketAddr, Response)>, 244 | recoverable_channel: bool, 245 | } 246 | impl HandlerDriver 247 | where 248 | H: HandleMessage, 249 | T: StunTransport, 250 | { 251 | fn new( 252 | spawner: BoxSpawn, 253 | handler: H, 254 | channel: Channel, 255 | recoverable_channel: bool, 256 | ) -> Self { 257 | let (response_tx, response_rx) = mpsc::channel(); 258 | HandlerDriver { 259 | spawner, 260 | handler, 261 | channel, 262 | response_tx, 263 | response_rx, 264 | recoverable_channel, 265 | } 266 | } 267 | 268 | fn handle_message( 269 | &mut self, 270 | peer: SocketAddr, 271 | message: RecvMessage, 272 | ) -> Result<()> { 273 | match message { 274 | RecvMessage::Indication(m) => self.handle_indication(peer, m), 275 | RecvMessage::Request(m) => track!(self.handle_request(peer, m))?, 276 | RecvMessage::Invalid(m) => track!(self.handle_invalid_message(peer, m))?, 277 | } 278 | Ok(()) 279 | } 280 | 281 | fn handle_indication(&mut self, peer: SocketAddr, indication: Indication) { 282 | match self.handler.handle_cast(peer, indication) { 283 | Action::NoReply => {} 284 | Action::FutureNoReply(future) => self.spawner.spawn(future.map_err(|_| unreachable!())), 285 | _ => unreachable!(), 286 | } 287 | } 288 | 289 | fn handle_request(&mut self, peer: SocketAddr, request: Request) -> Result<()> { 290 | match self.handler.handle_call(peer, request) { 291 | Action::NoReply => {} 292 | Action::FutureNoReply(future) => self.spawner.spawn(future.map_err(|_| unreachable!())), 293 | Action::Reply(m) => track!(self.channel.reply(peer, m))?, 294 | Action::FutureReply(future) => { 295 | let tx = self.response_tx.clone(); 296 | self.spawner.spawn( 297 | future 298 | .map(move |response| { 299 | let _ = tx.send((peer, response)); 300 | }) 301 | .map_err(|_| unreachable!()), 302 | ); 303 | } 304 | } 305 | Ok(()) 306 | } 307 | 308 | fn handle_invalid_message(&mut self, peer: SocketAddr, message: InvalidMessage) -> Result<()> { 309 | match self.handler.handle_invalid_message(peer, message) { 310 | Action::NoReply => {} 311 | Action::FutureNoReply(future) => self.spawner.spawn(future.map_err(|_| unreachable!())), 312 | Action::Reply(m) => track!(self.channel.reply(peer, m))?, 313 | Action::FutureReply(future) => { 314 | let tx = self.response_tx.clone(); 315 | self.spawner.spawn( 316 | future 317 | .map(move |response| { 318 | let _ = tx.send((peer, response)); 319 | }) 320 | .map_err(|_| unreachable!()), 321 | ); 322 | } 323 | } 324 | Ok(()) 325 | } 326 | } 327 | impl Future for HandlerDriver 328 | where 329 | H: HandleMessage, 330 | T: StunTransport, 331 | { 332 | type Item = (); 333 | type Error = Error; 334 | 335 | fn poll(&mut self) -> Poll { 336 | let mut did_something = true; 337 | while did_something { 338 | did_something = false; 339 | 340 | match track!(self.channel.poll_recv()) { 341 | Err(e) => { 342 | self.handler.handle_channel_error(&e); 343 | if !self.recoverable_channel { 344 | return Err(e); 345 | } 346 | did_something = true; 347 | } 348 | Ok(Async::NotReady) => {} 349 | Ok(Async::Ready(None)) => return Ok(Async::Ready(())), 350 | Ok(Async::Ready(Some((peer, message)))) => { 351 | track!(self.handle_message(peer, message))?; 352 | did_something = true; 353 | } 354 | } 355 | if let Err(e) = track!(self.channel.poll_send()) { 356 | self.handler.handle_channel_error(&e); 357 | return Err(e); 358 | } 359 | if let Async::Ready(item) = self.response_rx.poll().expect("never fails") { 360 | let (peer, response) = item.expect("never fails"); 361 | track!(self.channel.reply(peer, response))?; 362 | did_something = true; 363 | } 364 | } 365 | Ok(Async::NotReady) 366 | } 367 | } 368 | 369 | /// Example `BINDING` request handler. 370 | /// 371 | /// Note that this is provided only for test and example purposes. 372 | #[derive(Debug, Default, Clone)] 373 | pub struct BindingHandler; 374 | impl HandleMessage for BindingHandler { 375 | type Attribute = rfc5389::Attribute; 376 | 377 | fn handle_call( 378 | &mut self, 379 | peer: SocketAddr, 380 | request: Request, 381 | ) -> Action> { 382 | if request.method() == rfc5389::methods::BINDING { 383 | let mut response = SuccessResponse::new(&request); 384 | response.add_attribute(rfc5389::attributes::XorMappedAddress::new(peer).into()); 385 | Action::Reply(Ok(response)) 386 | } else { 387 | let response = ErrorResponse::new(&request, rfc5389::errors::BadRequest.into()); 388 | Action::Reply(Err(response)) 389 | } 390 | } 391 | 392 | fn handle_channel_error(&mut self, error: &Error) { 393 | eprintln!("[ERROR] {error}"); 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /src/transport/mod.rs: -------------------------------------------------------------------------------- 1 | //! Transport layer abstractions and its built-in implementations. 2 | use fibers_transport::{FixedPeerTransporter, PeerAddr, Result, Transport}; 3 | use stun_codec::{Attribute, DecodedMessage, Message, TransactionId}; 4 | 5 | pub use self::tcp::StunTcpTransporter; 6 | pub use self::udp::{StunUdpTransporter, StunUdpTransporterBuilder}; 7 | 8 | mod tcp; 9 | mod udp; 10 | 11 | /// This trait allows the implementation to be used as the transport layer for STUN. 12 | pub trait StunTransport: Transport, RecvItem = DecodedMessage> 13 | where 14 | A: Attribute, 15 | { 16 | /// Finishes a request/response transaction. 17 | fn finish_transaction( 18 | &mut self, 19 | peer: &Self::PeerAddr, 20 | transaction_id: TransactionId, 21 | ) -> Result<()>; 22 | } 23 | impl StunTransport for FixedPeerTransporter 24 | where 25 | A: Attribute, 26 | T: StunTransport, 27 | P: PeerAddr, 28 | { 29 | fn finish_transaction(&mut self, _peer: &P, transaction_id: TransactionId) -> Result<()> { 30 | let peer = self.interior_peer().clone(); 31 | track!(self.inner_mut().finish_transaction(&peer, transaction_id)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/transport/tcp.rs: -------------------------------------------------------------------------------- 1 | use fibers_transport::{PollRecv, PollSend, Result, TcpTransport, Transport}; 2 | use stun_codec::{Attribute, DecodedMessage, Message, TransactionId}; 3 | 4 | use super::StunTransport; 5 | 6 | /// TCP transport layer that can be used for STUN. 7 | #[derive(Debug)] 8 | pub struct StunTcpTransporter { 9 | inner: T, 10 | } 11 | impl StunTcpTransporter 12 | where 13 | A: Attribute, 14 | T: TcpTransport, RecvItem = DecodedMessage>, 15 | { 16 | /// Makes a new `StunTcpTransporter` instance. 17 | pub fn new(inner: T) -> Self { 18 | StunTcpTransporter { inner } 19 | } 20 | 21 | /// Returns a reference to the inner transporter. 22 | pub fn inner_ref(&self) -> &T { 23 | &self.inner 24 | } 25 | 26 | /// Returns a mutable reference to the inner transporter. 27 | pub fn inner_mut(&mut self) -> &mut T { 28 | &mut self.inner 29 | } 30 | } 31 | impl Transport for StunTcpTransporter 32 | where 33 | A: Attribute, 34 | T: TcpTransport, RecvItem = DecodedMessage>, 35 | { 36 | type PeerAddr = (); 37 | type SendItem = Message; 38 | type RecvItem = DecodedMessage; 39 | 40 | fn start_send(&mut self, (): Self::PeerAddr, item: Self::SendItem) -> Result<()> { 41 | track!(self.inner.start_send((), item)) 42 | } 43 | 44 | fn poll_send(&mut self) -> PollSend { 45 | track!(self.inner.poll_send()) 46 | } 47 | 48 | fn poll_recv(&mut self) -> PollRecv<(Self::PeerAddr, Self::RecvItem)> { 49 | track!(self.inner.poll_recv()) 50 | } 51 | } 52 | impl StunTransport for StunTcpTransporter 53 | where 54 | A: Attribute, 55 | T: TcpTransport, RecvItem = DecodedMessage>, 56 | { 57 | fn finish_transaction(&mut self, _peer: &(), _transaction_id: TransactionId) -> Result<()> { 58 | Ok(()) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/transport/udp.rs: -------------------------------------------------------------------------------- 1 | use fibers_timeout_queue::TimeoutQueue; 2 | use fibers_transport::{PollRecv, PollSend, Result, Transport, UdpTransport}; 3 | use std::collections::{HashMap, HashSet, VecDeque}; 4 | use std::net::SocketAddr; 5 | use std::time::{Duration, SystemTime, UNIX_EPOCH}; 6 | use stun_codec::{Attribute, DecodedMessage, Message, MessageClass, TransactionId}; 7 | 8 | use super::StunTransport; 9 | 10 | /// [`StunUdpTransporter`] builder. 11 | /// 12 | /// [`StunUdpTransporter`]: ./struct.StunUdpTransporter.html 13 | #[derive(Debug, Clone)] 14 | pub struct StunUdpTransporterBuilder { 15 | rto: Duration, 16 | rto_cache_duration: Duration, 17 | min_transaction_interval: Duration, 18 | max_outstanding_transactions: usize, 19 | } 20 | impl StunUdpTransporterBuilder { 21 | /// The default value of RTO (Retransmission TimeOut). 22 | /// 23 | /// > A client SHOULD retransmit a STUN request message starting with an 24 | /// > interval of RTO ("Retransmission TimeOut"), doubling after each 25 | /// > retransmission. The RTO is an estimate of the round-trip time (RTT), 26 | /// > and is computed as described in RFC 2988 [RFC2988], with two 27 | /// > exceptions. First, the initial value for RTO SHOULD be configurable 28 | /// > (rather than the 3 s recommended in RFC 2988) and SHOULD be greater 29 | /// > than **500 ms**. 30 | /// > 31 | /// > [RFC 5389 -- 7.2.1. Sending over UDP] 32 | /// 33 | /// [RFC 5389 -- 7.2.1. Sending over UDP]: https://tools.ietf.org/html/rfc5389#section-7.2.1 34 | pub const DEFAULT_RTO_MS: u64 = 500; 35 | 36 | /// The default duration preserving a cached RTO (Retransmission TimeOut). 37 | /// 38 | /// > The value for RTO SHOULD be cached by a client after the completion 39 | /// > of the transaction, and used as the starting value for RTO for the 40 | /// > next transaction to the same server (based on equality of IP 41 | /// > address). The value SHOULD be considered stale and discarded after 42 | /// > **10 minutes**. 43 | /// > 44 | /// > [RFC 5389 -- 7.2.1. Sending over UDP] 45 | /// 46 | /// [RFC 5389 -- 7.2.1. Sending over UDP]: https://tools.ietf.org/html/rfc5389#section-7.2.1 47 | pub const DEFAULT_RTO_CACHE_DURATION_MS: u64 = 10 * 60 * 1000; 48 | 49 | /// The default max concurrent transactions by a client to a server. 50 | /// 51 | /// > At any time, a client MAY have multiple outstanding STUN requests 52 | /// > with the same STUN server (that is, multiple transactions in 53 | /// > progress, with different transaction IDs). Absent other limits to 54 | /// > the rate of new transactions (such as those specified by ICE for 55 | /// > connectivity checks or when STUN is run over TCP), a client SHOULD 56 | /// > space new transactions to a server by RTO and SHOULD limit itself to 57 | /// > **ten outstanding transactions** to the same server. 58 | /// > 59 | /// > [RFC 5389 -- 7.2. Sending the Request or Indication] 60 | /// 61 | /// [RFC 5389 -- 7.2. Sending the Request or Indication]: https://tools.ietf.org/html/rfc5389#section-7.2 62 | pub const DEFAULT_MAX_OUTSTANDING_TRANSACTIONS: usize = 10; 63 | 64 | /// The default interval between transactions issued by a client to a serve. 65 | /// 66 | /// > At any time, a client MAY have multiple outstanding STUN requests 67 | /// > with the same STUN server (that is, multiple transactions in 68 | /// > progress, with different transaction IDs). Absent other limits to 69 | /// > the rate of new transactions (such as those specified by ICE for 70 | /// > connectivity checks or when STUN is run over TCP), **a client SHOULD 71 | /// > space new transactions to a server by RTO** and SHOULD limit itself to 72 | /// > ten outstanding transactions to the same server. 73 | /// > 74 | /// > [RFC 5389 -- 7.2. Sending the Request or Indication] 75 | /// 76 | /// [RFC 5389 -- 7.2. Sending the Request or Indication]: https://tools.ietf.org/html/rfc5389#section-7.2 77 | pub const DEFAULT_MIN_TRANSACTION_INTERVAL_MS: u64 = Self::DEFAULT_RTO_MS; 78 | 79 | /// Makes a new `StunUdpTransporterBuilder` instance with the default settings. 80 | pub fn new() -> Self { 81 | Self::default() 82 | } 83 | 84 | /// Sets the RTO of the resulting instance. 85 | /// 86 | /// The default value is `Duration::from_millis(DEFAULT_RTO_MS)`. 87 | pub fn rto(&mut self, rto: Duration) -> &mut Self { 88 | self.rto = rto; 89 | self 90 | } 91 | 92 | /// Sets the RTO cache duration of the resulting instance. 93 | /// 94 | /// The default value is `Duration::from_millis(DEFAULT_RTO_CACHE_DURATION_MS)`. 95 | pub fn rto_cache_duration(&mut self, duration: Duration) -> &mut Self { 96 | self.rto_cache_duration = duration; 97 | self 98 | } 99 | 100 | /// Sets the minimum interval of the consecutive request/response transactions of 101 | /// the resulting instance. 102 | /// 103 | /// The default value is `Duration::from_millis(DEFAULT_MIN_TRANSACTION_INTERVAL_MS)`. 104 | pub fn min_transaction_interval(&mut self, interval: Duration) -> &mut Self { 105 | self.min_transaction_interval = interval; 106 | self 107 | } 108 | 109 | /// Sets the number of the maximum outstanding transactions of the resulting instance. 110 | /// 111 | /// The default value is `DEFAULT_MAX_OUTSTANDING_TRANSACTIONS`. 112 | pub fn max_outstanding_transactions(&mut self, max: usize) -> &mut Self { 113 | self.max_outstanding_transactions = max; 114 | self 115 | } 116 | 117 | /// Makes a new `StunUdpTransporter` instance with the given settings. 118 | pub fn finish(&self, inner: T) -> StunUdpTransporter 119 | where 120 | A: Attribute, 121 | T: UdpTransport, RecvItem = DecodedMessage>, 122 | { 123 | let inner = RetransmitTransporter { 124 | inner, 125 | timeout_queue: TimeoutQueue::new(), 126 | peers: HashMap::new(), 127 | rto: self.rto, 128 | rto_cache_duration: self.rto_cache_duration, 129 | min_transaction_interval: self.min_transaction_interval, 130 | max_outstanding_transactions: self.max_outstanding_transactions, 131 | }; 132 | StunUdpTransporter { inner } 133 | } 134 | } 135 | impl Default for StunUdpTransporterBuilder { 136 | fn default() -> Self { 137 | StunUdpTransporterBuilder { 138 | rto: Duration::from_millis(Self::DEFAULT_RTO_MS), 139 | rto_cache_duration: Duration::from_millis(Self::DEFAULT_RTO_CACHE_DURATION_MS), 140 | min_transaction_interval: Duration::from_millis( 141 | Self::DEFAULT_MIN_TRANSACTION_INTERVAL_MS, 142 | ), 143 | max_outstanding_transactions: Self::DEFAULT_MAX_OUTSTANDING_TRANSACTIONS, 144 | } 145 | } 146 | } 147 | 148 | /// UDP transport layer that can be used for STUN. 149 | #[derive(Debug)] 150 | pub struct StunUdpTransporter { 151 | inner: RetransmitTransporter, 152 | } 153 | impl StunUdpTransporter 154 | where 155 | A: Attribute, 156 | T: UdpTransport, RecvItem = DecodedMessage>, 157 | { 158 | /// Makes a new `StunUdpTransporter` instance. 159 | /// 160 | /// This is equivalent to `StunUdpTransporterBuilder::new().finish(inner)`. 161 | pub fn new(inner: T) -> Self { 162 | StunUdpTransporterBuilder::new().finish(inner) 163 | } 164 | 165 | /// Returns a reference to the inner transporter. 166 | pub fn inner_ref(&self) -> &T { 167 | &self.inner.inner 168 | } 169 | 170 | /// Returns a mutable reference to the inner transporter. 171 | pub fn inner_mut(&mut self) -> &mut T { 172 | &mut self.inner.inner 173 | } 174 | } 175 | impl Transport for StunUdpTransporter 176 | where 177 | A: Attribute, 178 | T: UdpTransport, RecvItem = DecodedMessage>, 179 | { 180 | type PeerAddr = SocketAddr; 181 | type SendItem = Message; 182 | type RecvItem = DecodedMessage; 183 | 184 | fn start_send(&mut self, peer: Self::PeerAddr, item: Self::SendItem) -> Result<()> { 185 | track!(self.inner.start_send(peer, item)) 186 | } 187 | 188 | fn poll_send(&mut self) -> PollSend { 189 | track!(self.inner.poll_send()) 190 | } 191 | 192 | fn poll_recv(&mut self) -> PollRecv<(Self::PeerAddr, Self::RecvItem)> { 193 | track!(self.inner.poll_recv()) 194 | } 195 | } 196 | impl StunTransport for StunUdpTransporter 197 | where 198 | A: Attribute, 199 | T: UdpTransport, RecvItem = DecodedMessage>, 200 | { 201 | fn finish_transaction( 202 | &mut self, 203 | peer: &SocketAddr, 204 | transaction_id: TransactionId, 205 | ) -> Result<()> { 206 | track!(self.inner.finish_transaction(peer, transaction_id)) 207 | } 208 | } 209 | 210 | /// An implementation of [`StunTransport`] that retransmits request messages for improving reliability. 211 | /// 212 | /// [`StunTransport`]: ./trait.StunTransport.html 213 | #[derive(Debug)] 214 | struct RetransmitTransporter { 215 | inner: T, 216 | timeout_queue: TimeoutQueue>, 217 | peers: HashMap>, 218 | rto: Duration, 219 | rto_cache_duration: Duration, 220 | min_transaction_interval: Duration, 221 | max_outstanding_transactions: usize, 222 | } 223 | impl RetransmitTransporter 224 | where 225 | A: Attribute, 226 | T: UdpTransport, RecvItem = DecodedMessage>, 227 | { 228 | fn waiting_time(&self, peer: SocketAddr) -> Option { 229 | self.peers[&peer] 230 | .last_transaction_start_time 231 | .elapsed() 232 | .ok() 233 | .and_then(|d| self.min_transaction_interval.checked_sub(d)) 234 | } 235 | 236 | fn peer_mut(&mut self, peer: SocketAddr) -> &mut PeerState { 237 | self.peers.get_mut(&peer).expect("never fails") 238 | } 239 | 240 | #[allow(clippy::map_entry)] 241 | fn start_transaction( 242 | &mut self, 243 | peer: SocketAddr, 244 | request: Message, 245 | first: bool, 246 | ) -> Result<()> { 247 | if !self.peers.contains_key(&peer) { 248 | self.peers.insert(peer, PeerState::new(peer, self.rto)); 249 | } 250 | 251 | if self.peers[&peer].waiting { 252 | self.peer_mut(peer).pending(request, first); 253 | } else if let Some(duration) = self.waiting_time(peer) { 254 | self.peer_mut(peer).waiting = true; 255 | self.timeout_queue 256 | .push(TimeoutEntry::AllowNextRequest { peer }, duration); 257 | self.peer_mut(peer).pending(request, first); 258 | } else if self.peers[&peer].transactions.len() >= self.max_outstanding_transactions { 259 | self.peer_mut(peer).pending(request, first); 260 | } else { 261 | track!(self.inner.start_send(peer, request.clone()))?; 262 | let timeout = self.peer_mut(peer).start_transaction(request); 263 | self.timeout_queue.push(timeout.0, timeout.1); 264 | } 265 | Ok(()) 266 | } 267 | 268 | fn poll_timeout(&mut self) -> Option> { 269 | let peers = &self.peers; 270 | self.timeout_queue.filter_pop(|entry| { 271 | if let TimeoutEntry::Retransmit { peer, request, .. } = entry { 272 | peers.get(peer).map_or(false, |p| { 273 | p.transactions.contains(&request.transaction_id()) 274 | }) 275 | } else { 276 | true 277 | } 278 | }) 279 | } 280 | 281 | fn handle_pending_request(&mut self, peer: SocketAddr) -> Result<()> { 282 | if !self.peers.contains_key(&peer) { 283 | return Ok(()); 284 | } 285 | if let Some(request) = self.peer_mut(peer).pop_pending_request() { 286 | track!(self.start_transaction(peer, request, false))?; 287 | } 288 | if self.peers[&peer].is_idle() { 289 | self.peers.remove(&peer); 290 | } 291 | Ok(()) 292 | } 293 | 294 | fn handle_retransmit( 295 | &mut self, 296 | peer: SocketAddr, 297 | request: Message, 298 | rto: Duration, 299 | ) -> Result<()> { 300 | if let Some(p) = self.peers.get_mut(&peer) { 301 | if let Some(request) = p.retransmit( 302 | request, 303 | rto, 304 | self.rto_cache_duration, 305 | &mut self.timeout_queue, 306 | ) { 307 | track!(self.inner.start_send(peer, request))?; 308 | } 309 | } 310 | Ok(()) 311 | } 312 | } 313 | impl Transport for RetransmitTransporter 314 | where 315 | A: Attribute, 316 | T: UdpTransport, RecvItem = DecodedMessage>, 317 | { 318 | type PeerAddr = SocketAddr; 319 | type SendItem = Message; 320 | type RecvItem = DecodedMessage; 321 | 322 | fn start_send(&mut self, peer: SocketAddr, item: Self::SendItem) -> Result<()> { 323 | if item.class() == MessageClass::Request { 324 | track!(self.start_transaction(peer, item, true)) 325 | } else { 326 | track!(self.inner.start_send(peer, item)) 327 | } 328 | } 329 | 330 | fn poll_send(&mut self) -> PollSend { 331 | while let Some(entry) = self.poll_timeout() { 332 | match entry { 333 | TimeoutEntry::Retransmit { 334 | peer, 335 | request, 336 | next_rto, 337 | } => { 338 | track!(self.handle_retransmit(peer, request, next_rto))?; 339 | } 340 | TimeoutEntry::ExpireRtoCache { peer, cached_rto } => { 341 | if let Some(p) = self.peers.get_mut(&peer) { 342 | if p.cached_rto == cached_rto { 343 | p.cached_rto = self.rto; 344 | } 345 | } 346 | } 347 | TimeoutEntry::AllowNextRequest { peer } => { 348 | self.peer_mut(peer).waiting = false; 349 | track!(self.handle_pending_request(peer))?; 350 | } 351 | } 352 | } 353 | 354 | track!(self.inner.poll_send()) 355 | } 356 | 357 | fn poll_recv(&mut self) -> PollRecv<(Self::PeerAddr, Self::RecvItem)> { 358 | track!(self.inner.poll_recv()) 359 | } 360 | } 361 | impl StunTransport for RetransmitTransporter 362 | where 363 | A: Attribute, 364 | T: UdpTransport, RecvItem = DecodedMessage>, 365 | { 366 | fn finish_transaction( 367 | &mut self, 368 | peer: &SocketAddr, 369 | transaction_id: TransactionId, 370 | ) -> Result<()> { 371 | if let Some(p) = self.peers.get_mut(peer) { 372 | p.finish_transaction(transaction_id); 373 | } 374 | track!(self.handle_pending_request(*peer)) 375 | } 376 | } 377 | 378 | #[derive(Debug)] 379 | enum TimeoutEntry { 380 | Retransmit { 381 | peer: SocketAddr, 382 | request: Message, 383 | next_rto: Duration, 384 | }, 385 | ExpireRtoCache { 386 | peer: SocketAddr, 387 | cached_rto: Duration, 388 | }, 389 | AllowNextRequest { 390 | peer: SocketAddr, 391 | }, 392 | } 393 | 394 | #[derive(Debug)] 395 | struct PeerState { 396 | peer: SocketAddr, 397 | transactions: HashSet, 398 | pending_requests: VecDeque>, 399 | waiting: bool, 400 | last_transaction_start_time: SystemTime, 401 | cached_rto: Duration, 402 | } 403 | impl PeerState { 404 | fn new(peer: SocketAddr, rto: Duration) -> Self { 405 | PeerState { 406 | peer, 407 | transactions: HashSet::new(), 408 | pending_requests: VecDeque::new(), 409 | waiting: false, 410 | last_transaction_start_time: UNIX_EPOCH, 411 | cached_rto: rto, 412 | } 413 | } 414 | 415 | fn pending(&mut self, request: Message, first: bool) { 416 | if first { 417 | self.pending_requests.push_back(request); 418 | } else { 419 | self.pending_requests.push_front(request); 420 | } 421 | } 422 | 423 | fn is_idle(&self) -> bool { 424 | self.transactions.is_empty() && !self.waiting 425 | } 426 | 427 | fn pop_pending_request(&mut self) -> Option> { 428 | while let Some(request) = self.pending_requests.pop_front() { 429 | if self.transactions.contains(&request.transaction_id()) { 430 | return Some(request); 431 | } 432 | } 433 | None 434 | } 435 | 436 | fn retransmit( 437 | &mut self, 438 | request: Message, 439 | rto: Duration, 440 | rto_cache_duration: Duration, 441 | queue: &mut TimeoutQueue>, 442 | ) -> Option> { 443 | if self.transactions.contains(&request.transaction_id()) { 444 | queue.push( 445 | TimeoutEntry::Retransmit { 446 | peer: self.peer, 447 | request: request.clone(), 448 | next_rto: rto * 2, 449 | }, 450 | rto, 451 | ); 452 | if self.cached_rto < rto { 453 | self.cached_rto = rto; 454 | queue.push( 455 | TimeoutEntry::ExpireRtoCache { 456 | peer: self.peer, 457 | cached_rto: rto, 458 | }, 459 | rto_cache_duration, 460 | ); 461 | } 462 | Some(request) 463 | } else { 464 | None 465 | } 466 | } 467 | 468 | fn start_transaction(&mut self, request: Message) -> (TimeoutEntry, Duration) { 469 | self.transactions.insert(request.transaction_id()); 470 | self.last_transaction_start_time = SystemTime::now(); 471 | let entry = TimeoutEntry::Retransmit { 472 | peer: self.peer, 473 | request, 474 | next_rto: self.cached_rto * 2, 475 | }; 476 | (entry, self.cached_rto) 477 | } 478 | 479 | fn finish_transaction(&mut self, transaction_id: TransactionId) { 480 | self.transactions.remove(&transaction_id); 481 | } 482 | } 483 | --------------------------------------------------------------------------------