rustun/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::<MessageEncoder<_>, 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::<rfc5389::Attribute>::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]
74extern crate trackable;
75
76pub use error::{Error, ErrorKind};
77
78pub mod channel;
79pub mod client;
80pub mod message;
81pub mod server;
82pub mod transport;
83
84mod error;
85
86/// A specialized `Result` type for this crate.
87pub type Result<T> = std::result::Result<T, Error>;
88
89#[cfg(test)]
90mod 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::<MessageEncoder<_>, 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::<rfc5389::Attribute>::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::<BindingHandler>::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::<MessageEncoder<_>, 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::<rfc5389::Attribute>::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}