├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-BSD ├── LICENSE-MIT ├── README.md ├── examples ├── common │ ├── event_loop.rs │ ├── mod.rs │ └── types.rs ├── peer-config ├── peer.rs ├── peer_impl │ ├── active_peer.rs │ ├── event.rs │ ├── mod.rs │ └── overlay_connect.rs ├── server-config ├── server.rs └── server_impl │ ├── mod.rs │ ├── overlay.rs │ └── peer.rs ├── pre-commit-git-hook ├── rustfmt.toml ├── src ├── config.rs ├── error.rs ├── hole_punch.rs ├── lib.rs ├── queued_notifier.rs ├── tcp │ ├── hole_punch │ │ ├── listener.rs │ │ ├── mod.rs │ │ ├── puncher.rs │ │ └── rendezvous_client.rs │ ├── mod.rs │ └── rendezvous_server │ │ ├── exchange_msg.rs │ │ └── mod.rs ├── test_utils.rs └── udp │ ├── hole_punch │ ├── mod.rs │ ├── puncher.rs │ └── rendezvous_client.rs │ ├── mod.rs │ └── rendezvous_server.rs └── tests ├── nat-traversal-test-resources ├── config-peers ├── config-rendezvous-server-0 ├── config-rendezvous-server-1 ├── config-rendezvous-server-2 └── sample-log.toml └── nat_traversal.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Executables 2 | *.exe 3 | *.out 4 | 5 | # Libraires and Objects 6 | *.o 7 | *.a 8 | *.so 9 | *.lo 10 | *.lib 11 | *.dll 12 | *.rlib 13 | *.dylib 14 | 15 | # Generated by Cargo 16 | bin/ 17 | tags* 18 | *.lock 19 | build/ 20 | target*/ 21 | build-tests/ 22 | 23 | # Generated by Editors 24 | ~* 25 | *.swp 26 | *.sublime-* 27 | 28 | # Manual 29 | .cargo/ 30 | 31 | # Misc 32 | packages/ 33 | .DS_Store 34 | *.bootstrap.cache 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | - RUST_BACKTRACE=1 4 | - PATH=$PATH:$HOME/.cargo/bin 5 | os: 6 | - linux 7 | - osx 8 | - windows 9 | language: rust 10 | rust: 11 | - 1.30.1 12 | sudo: false 13 | cache: 14 | cargo: true 15 | before_script: 16 | - if ! cargo prune --version | grep -q "0.1.8"; then 17 | cargo install cargo-prune --vers="0.1.8" --force; 18 | fi 19 | - if ! rustfmt --version | grep -q "0.99.4-stable"; then 20 | rustup component add rustfmt-preview; 21 | if ! rustfmt --version | grep -q "0.99.4-stable"; then 22 | printf "Bad rustfmt version"; 23 | exit 1; 24 | fi 25 | fi 26 | - if ! cargo clippy --version | grep -q "0.0.212"; then 27 | rustup component add clippy-preview; 28 | if ! cargo clippy --version | grep -q "0.0.212"; then 29 | printf "Bad clippy version"; 30 | exit 1; 31 | fi 32 | fi 33 | script: 34 | - set -x; 35 | cargo test --verbose --release 36 | - if [ "${TRAVIS_OS_NAME}" = linux ]; then 37 | ( 38 | set -x; 39 | cargo fmt -- --check && 40 | cargo clippy --verbose --release && 41 | cargo clippy --verbose --release --profile=test ; 42 | ) 43 | fi 44 | before_cache: 45 | - cargo prune 46 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["ustulation "] 3 | description = "NAT Traversal for P2P communication" 4 | documentation = "https://docs.rs/p2p/" 5 | license = "MIT OR BSD-3-Clause" 6 | name = "p2p" 7 | readme = "README.md" 8 | repository = "https://github.com/ustulation/p2p" 9 | version = "0.6.0" 10 | 11 | [dependencies] 12 | bincode = "~0.8.0" 13 | byteorder = "~1.1.0" 14 | log = "~0.3.8" 15 | mio = "~0.6.9" 16 | mio-extras = "~2.0.5" 17 | net2 = "~0.2.30" 18 | quick-error = "~1.2.0" 19 | rand = "~0.3.16" 20 | rust_sodium = "~0.5.0" 21 | serde = "~1.0.11" 22 | serde_derive = "~1.0.11" 23 | unwrap = "~1.1.0" 24 | 25 | [dependencies.socket-collection] 26 | version = "~0.3.0" 27 | #git = "https://github.com/maidsafe/socket-collection" 28 | #branch = "community-release-1" 29 | 30 | [dev-dependencies] 31 | env_logger = "~0.5.13" 32 | maidsafe_utilities = "~0.15.0" 33 | serde_json = "~1.0.2" 34 | 35 | #[features] 36 | #enable-udt = ["socket-collection/enable-udt"] 37 | -------------------------------------------------------------------------------- /LICENSE-BSD: -------------------------------------------------------------------------------- 1 | Copyright 2018 MaidSafe.net limited. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2018 MaidSafe.net limited. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # P2P NAT-Traversal 2 | 3 | |Crate|Documentation|Linux/OSX/Windows| 4 | |:---:|:-----------:|:----------------:| 5 | | [![](http://meritbadge.herokuapp.com/p2p)](https://crates.io/crates/p2p) | [![Documentation](https://docs.rs/p2p/badge.svg)](https://docs.rs/p2p) | [![Build Status](https://travis-ci.org/ustulation/p2p.svg?branch=master)](https://travis-ci.org/ustulation/p2p) 6 | 7 | 8 | The goal of this crate is to provide a robust and crypto-secure NAT traversal for peer to peer connection. It assumes publicly reachable rendezvous servers are provided. The server code itself is in the crate too, so the crate can either be used to deploy a server or used for peer to peer client communication or both simultaneously - for e.g. if you run the server on a port forwarded endpoint, it will be publicly available for others to rendezvous while you could choose normal NAT traversal mechanisms to communicate with other peers. 9 | 10 | Please refer to the documentation above for detailed explanation. The examples and the integration tests show how the crate can be used. 11 | -------------------------------------------------------------------------------- /examples/common/event_loop.rs: -------------------------------------------------------------------------------- 1 | use mio::{Events, Poll, PollOpt, Ready, Token}; 2 | use mio_extras::channel::{self, Sender}; 3 | use mio_extras::timer::{Timeout, Timer}; 4 | use p2p::{Config, Interface, NatMsg, NatState, NatTimer}; 5 | use std::any::Any; 6 | // use socket_collection::{EpollLoop, Handle, Notifier}; 7 | use sodium::crypto::box_; 8 | use std::cell::RefCell; 9 | use std::collections::hash_map::Entry; 10 | use std::collections::HashMap; 11 | use std::rc::Rc; 12 | use std::thread::{self, JoinHandle}; 13 | use std::time::Duration; 14 | 15 | pub struct Core { 16 | nat_states: HashMap>>, 17 | peer_states: HashMap>>, 18 | core_timer: Timer, 19 | nat_timer: Timer, 20 | token: usize, 21 | config: Config, 22 | enc_pk: box_::PublicKey, 23 | enc_sk: box_::SecretKey, 24 | tx: Sender, 25 | // udt_epoll_handle: Handle, 26 | } 27 | 28 | impl Core { 29 | #[allow(unused)] 30 | pub fn insert_peer_state( 31 | &mut self, 32 | token: Token, 33 | state: Rc>, 34 | ) -> Result<(), (Rc>, String)> { 35 | if let Entry::Vacant(ve) = self.peer_states.entry(token) { 36 | ve.insert(state); 37 | Ok(()) 38 | } else { 39 | Err((state, "Token is already mapped".to_string())) 40 | } 41 | } 42 | 43 | pub fn remove_peer_state(&mut self, token: Token) -> Option>> { 44 | self.peer_states.remove(&token) 45 | } 46 | 47 | // pub fn udt_epoll_handle(&self) -> Handle { 48 | // unreachable!("For later"); 49 | // //self.udt_epoll_handle.clone() 50 | // } 51 | 52 | pub fn peer_state(&mut self, token: Token) -> Option>> { 53 | self.peer_states.get(&token).cloned() 54 | } 55 | 56 | pub fn set_core_timeout(&mut self, duration: Duration, timer_detail: CoreTimer) -> Timeout { 57 | self.core_timer.set_timeout(duration, timer_detail) 58 | } 59 | 60 | pub fn cancel_core_timeout(&mut self, timeout: &Timeout) -> Option { 61 | self.core_timer.cancel_timeout(timeout) 62 | } 63 | 64 | fn handle_core_timer(&mut self, poll: &Poll) { 65 | while let Some(core_timer) = self.core_timer.poll() { 66 | if let Some(peer_state) = self.peer_state(core_timer.associated_peer_state) { 67 | peer_state 68 | .borrow_mut() 69 | .timeout(self, poll, core_timer.timer_id); 70 | } 71 | } 72 | } 73 | 74 | fn handle_nat_timer(&mut self, poll: &Poll) { 75 | while let Some(nat_timer) = self.nat_timer.poll() { 76 | if let Some(nat_state) = self.state(nat_timer.associated_nat_state) { 77 | nat_state 78 | .borrow_mut() 79 | .timeout(self, poll, nat_timer.timer_id); 80 | } 81 | } 82 | } 83 | 84 | fn handle_readiness(&mut self, poll: &Poll, token: Token, kind: Ready) { 85 | if let Some(nat_state) = self.state(token) { 86 | return nat_state.borrow_mut().ready(self, poll, kind); 87 | } 88 | if let Some(peer_state) = self.peer_states.get(&token).cloned() { 89 | return peer_state.borrow_mut().ready(self, poll, kind); 90 | } 91 | } 92 | } 93 | 94 | impl Interface for Core { 95 | fn insert_state( 96 | &mut self, 97 | token: Token, 98 | state: Rc>, 99 | ) -> Result<(), (Rc>, String)> { 100 | if let Entry::Vacant(ve) = self.nat_states.entry(token) { 101 | ve.insert(state); 102 | Ok(()) 103 | } else { 104 | Err((state, "Token is already mapped".to_string())) 105 | } 106 | } 107 | 108 | fn remove_state(&mut self, token: Token) -> Option>> { 109 | self.nat_states.remove(&token) 110 | } 111 | 112 | fn state(&mut self, token: Token) -> Option>> { 113 | self.nat_states.get(&token).cloned() 114 | } 115 | 116 | fn set_timeout(&mut self, duration: Duration, timer_detail: NatTimer) -> Timeout { 117 | self.nat_timer.set_timeout(duration, timer_detail) 118 | } 119 | 120 | fn cancel_timeout(&mut self, timeout: &Timeout) -> Option { 121 | self.nat_timer.cancel_timeout(timeout) 122 | } 123 | 124 | fn new_token(&mut self) -> Token { 125 | self.token += 1; 126 | Token(self.token) 127 | } 128 | 129 | fn config(&self) -> &Config { 130 | &self.config 131 | } 132 | 133 | fn enc_pk(&self) -> &box_::PublicKey { 134 | &self.enc_pk 135 | } 136 | 137 | fn enc_sk(&self) -> &box_::SecretKey { 138 | &self.enc_sk 139 | } 140 | 141 | fn sender(&self) -> &Sender { 142 | &self.tx 143 | } 144 | 145 | fn as_any(&mut self) -> &mut Any { 146 | self 147 | } 148 | } 149 | 150 | pub trait CoreState { 151 | fn ready(&mut self, &mut Core, &Poll, Ready); 152 | fn write(&mut self, &mut Core, &Poll, Vec) {} 153 | fn timeout(&mut self, &mut Core, &Poll, u8) {} 154 | fn terminate(&mut self, &mut Core, &Poll); 155 | fn as_any(&mut self) -> &mut Any; 156 | } 157 | 158 | pub struct CoreTimer { 159 | pub associated_peer_state: Token, 160 | pub timer_id: u8, 161 | } 162 | 163 | impl CoreTimer { 164 | pub fn new(associated_peer_state: Token, timer_id: u8) -> Self { 165 | CoreTimer { 166 | associated_peer_state, 167 | timer_id, 168 | } 169 | } 170 | } 171 | pub struct CoreMsg(Option>); 172 | impl CoreMsg { 173 | pub fn new(f: F) -> Self { 174 | let mut f = Some(f); 175 | CoreMsg(Some(Box::new(move |core: &mut Core, poll: &Poll| { 176 | if let Some(f) = f.take() { 177 | f(core, poll); 178 | } 179 | }))) 180 | } 181 | } 182 | 183 | // pub struct Notify(Sender); 184 | // impl Notifier for Notify { 185 | // fn notify(&self, event: Event) { 186 | // unwrap!(self.0.send(CoreMsg::new(move |core, poll| { 187 | // core.handle_readiness(poll, event.token(), event.kind()); 188 | // }))); 189 | // } 190 | // } 191 | 192 | pub struct El { 193 | pub nat_tx: Sender, 194 | pub core_tx: Sender, 195 | joiner: Option>, 196 | } 197 | 198 | impl Drop for El { 199 | fn drop(&mut self) { 200 | let _ = self.core_tx.send(CoreMsg(None)); 201 | let joiner = unwrap!(self.joiner.take()); 202 | unwrap!(joiner.join()); 203 | println!("Gracefully shut down mio event loop"); 204 | } 205 | } 206 | 207 | pub fn spawn_event_loop(p2p_cfg: Config) -> El { 208 | let (core_tx, core_rx) = channel::channel::(); 209 | let (nat_tx, nat_rx) = channel::channel(); 210 | let nat_tx_cloned = nat_tx.clone(); 211 | let core_tx_cloned = core_tx.clone(); 212 | 213 | let joiner = thread::spawn(move || { 214 | const CORE_TIMER_TOKEN: usize = 0; 215 | const NAT_TIMER_TOKEN: usize = CORE_TIMER_TOKEN + 1; 216 | const CORE_RX_TOKEN: usize = NAT_TIMER_TOKEN + 1; 217 | const NAT_RX_TOKEN: usize = CORE_RX_TOKEN + 1; 218 | 219 | let poll = unwrap!(Poll::new()); 220 | 221 | let (enc_pk, enc_sk) = box_::gen_keypair(); 222 | let core_timer = Timer::default(); 223 | let nat_timer = Timer::default(); 224 | 225 | unwrap!(poll.register( 226 | &core_timer, 227 | Token(CORE_TIMER_TOKEN), 228 | Ready::readable() | Ready::error() | Ready::hup(), 229 | PollOpt::edge(), 230 | )); 231 | unwrap!(poll.register( 232 | &nat_timer, 233 | Token(NAT_TIMER_TOKEN), 234 | Ready::readable() | Ready::error() | Ready::hup(), 235 | PollOpt::edge(), 236 | )); 237 | unwrap!(poll.register( 238 | &core_rx, 239 | Token(CORE_RX_TOKEN), 240 | Ready::readable() | Ready::error() | Ready::hup(), 241 | PollOpt::edge(), 242 | )); 243 | unwrap!(poll.register( 244 | &nat_rx, 245 | Token(NAT_RX_TOKEN), 246 | Ready::readable() | Ready::error() | Ready::hup(), 247 | PollOpt::edge(), 248 | )); 249 | 250 | // let notifier = Notify(core_tx); 251 | // let epoll_loop = unwrap!(EpollLoop::start_event_loop(notifier)); 252 | // let udt_epoll_handle = epoll_loop.handle(); 253 | 254 | let mut core = Core { 255 | nat_states: HashMap::with_capacity(10), 256 | peer_states: HashMap::with_capacity(5), 257 | core_timer, 258 | nat_timer, 259 | token: NAT_RX_TOKEN + 1, 260 | config: p2p_cfg, 261 | enc_pk: enc_pk, 262 | enc_sk: enc_sk, 263 | tx: nat_tx, 264 | // udt_epoll_handle, 265 | }; 266 | 267 | let mut events = Events::with_capacity(1024); 268 | 269 | 'event_loop: loop { 270 | unwrap!(poll.poll(&mut events, None)); 271 | 272 | for event in events.iter() { 273 | match event.token() { 274 | Token(t) if t == CORE_TIMER_TOKEN => { 275 | assert!(event.kind().is_readable()); 276 | core.handle_core_timer(&poll); 277 | } 278 | Token(t) if t == NAT_TIMER_TOKEN => { 279 | assert!(event.kind().is_readable()); 280 | core.handle_nat_timer(&poll); 281 | } 282 | Token(t) if t == CORE_RX_TOKEN => { 283 | assert!(event.kind().is_readable()); 284 | while let Ok(f) = core_rx.try_recv() { 285 | if let Some(mut f) = f.0 { 286 | f(&mut core, &poll); 287 | } else { 288 | break 'event_loop; 289 | } 290 | } 291 | } 292 | Token(t) if t == NAT_RX_TOKEN => { 293 | assert!(event.kind().is_readable()); 294 | while let Ok(f) = nat_rx.try_recv() { 295 | f.invoke(&mut core, &poll); 296 | } 297 | } 298 | t => core.handle_readiness(&poll, t, event.kind()), 299 | } 300 | } 301 | } 302 | }); 303 | 304 | El { 305 | nat_tx: nat_tx_cloned, 306 | core_tx: core_tx_cloned, 307 | joiner: Some(joiner), 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /examples/common/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod event_loop; 2 | pub mod types; 3 | 4 | use serde::de::DeserializeOwned; 5 | use serde_json; 6 | use std::fs::File; 7 | use std::io::Read; 8 | 9 | pub fn read_config(path: &str) -> T { 10 | let mut file = unwrap!(File::open(path)); 11 | let mut content = String::new(); 12 | unwrap!(file.read_to_string(&mut content)); 13 | unwrap!(serde_json::from_str(&content)) 14 | } 15 | -------------------------------------------------------------------------------- /examples/common/types.rs: -------------------------------------------------------------------------------- 1 | use p2p::RendezvousInfo; 2 | use sodium::crypto::box_; 3 | use std::fmt; 4 | 5 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] 6 | pub struct PeerId { 7 | pub name: String, 8 | pub pk: box_::PublicKey, 9 | } 10 | 11 | impl PeerId { 12 | pub fn new(name: String, pk: box_::PublicKey) -> Self { 13 | Self { name, pk } 14 | } 15 | } 16 | 17 | impl fmt::Display for PeerId { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 19 | write!( 20 | f, 21 | "Peer: {} ({:02x}{:02x}{:02x}{:02x})", 22 | self.name, self.pk.0[0], self.pk.0[1], self.pk.0[2], self.pk.0[3] 23 | ) 24 | } 25 | } 26 | 27 | #[derive(Debug, Serialize, Deserialize)] 28 | pub enum PlainTextMsg { 29 | ReqUpdateName(String), 30 | UpdateNameResp(bool), 31 | ReqOnlinePeers, 32 | OnlinePeersResp(Vec), 33 | ExchgRendezvousInfo { 34 | src_info: RendezvousInfo, 35 | dst_peer: PeerId, 36 | }, 37 | FwdRendezvousInfo { 38 | src_info: RendezvousInfo, 39 | src_peer: PeerId, 40 | }, 41 | Chat(String), 42 | } 43 | 44 | #[derive(Serialize, Deserialize)] 45 | pub enum PeerMsg { 46 | PubKey(box_::PublicKey), 47 | CipherText(Vec), 48 | } 49 | -------------------------------------------------------------------------------- /examples/peer-config: -------------------------------------------------------------------------------- 1 | { 2 | "peer_cfg": { 3 | "overlay_addr": "127.0.0.1:20001" 4 | }, 5 | "p2p_cfg": { 6 | "rendezvous_timeout_sec": null, 7 | "hole_punch_timeout_sec": 30, 8 | "hole_punch_wait_for_other": null, 9 | "udp_rendezvous_port": null, 10 | "tcp_rendezvous_port": null, 11 | "remote_udp_rendezvous_servers": [ 12 | "127.0.0.1:20010", 13 | "127.0.0.1:20011", 14 | "127.0.0.1:20012" 15 | ], 16 | "remote_tcp_rendezvous_servers": [ 17 | "127.0.0.1:20110", 18 | "127.0.0.1:20111", 19 | "127.0.0.1:20112" 20 | ], 21 | "udp_hole_punchers": [ 22 | { 23 | "starting_ttl": 2, 24 | "ttl_increment_delay_ms": 500 25 | }, 26 | { 27 | "starting_ttl": 5, 28 | "ttl_increment_delay_ms": 500 29 | }, 30 | { 31 | "starting_ttl": 64, 32 | "ttl_increment_delay_ms": 500 33 | } 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/peer.rs: -------------------------------------------------------------------------------- 1 | #![allow(deprecated)] 2 | 3 | #[macro_use] 4 | extern crate log; 5 | extern crate maidsafe_utilities; 6 | extern crate mio; 7 | extern crate mio_extras; 8 | extern crate p2p; 9 | extern crate rust_sodium as sodium; 10 | extern crate serde; 11 | extern crate serde_json; 12 | #[macro_use] 13 | extern crate serde_derive; 14 | extern crate socket_collection; 15 | #[macro_use] 16 | extern crate unwrap; 17 | 18 | pub use peer_impl::{entry_point, ActivePeer, Event, OverlayConnect, PeerState}; 19 | 20 | mod common; 21 | mod peer_impl; 22 | 23 | fn main() { 24 | unwrap!(maidsafe_utilities::log::init(true)); 25 | entry_point(); 26 | } 27 | -------------------------------------------------------------------------------- /examples/peer_impl/active_peer.rs: -------------------------------------------------------------------------------- 1 | use common::event_loop::{Core, CoreState, CoreTimer}; 2 | use common::types::{PeerId, PeerMsg, PlainTextMsg}; 3 | use maidsafe_utilities::serialisation::deserialise; 4 | use mio::{Poll, Ready, Token}; 5 | use mio_extras::timer::Timeout; 6 | use p2p::{msg_to_read, msg_to_send, Interface}; 7 | use socket_collection::UdpSock; 8 | use sodium::crypto::box_; 9 | use std::any::Any; 10 | use std::cell::RefCell; 11 | use std::collections::BTreeMap; 12 | use std::rc::Rc; 13 | use std::sync::mpsc::Sender; 14 | use std::sync::{Arc, Mutex}; 15 | use std::time::Duration; 16 | use {Event, PeerState}; 17 | 18 | const INACTIVITY_TIMEOUT_ID: u8 = 0; 19 | const TOLERATE_READ_ERRS_ID: u8 = 1; 20 | 21 | const INACTIVITY_TIMEOUT_SECS: u64 = 180; 22 | const TOLERATE_READ_ERRS_SECS: u64 = 60; 23 | 24 | pub struct ActivePeer { 25 | token: Token, 26 | sock: UdpSock, 27 | peer: PeerId, 28 | key: box_::PrecomputedKey, 29 | peers: Arc>>, 30 | should_buffer: bool, 31 | chat_buf: Vec, 32 | tolerate_read_errs: bool, 33 | timeout_inactivity: Timeout, 34 | _timeout_tolerate_read_errs: Timeout, 35 | tx: Sender, 36 | } 37 | 38 | impl ActivePeer { 39 | pub fn start( 40 | core: &mut Core, 41 | poll: &Poll, 42 | token: Token, 43 | sock: UdpSock, 44 | peer: PeerId, 45 | peers: Arc>>, 46 | tx: Sender, 47 | ) { 48 | let state = Rc::new(RefCell::new(ActivePeer { 49 | token, 50 | sock, 51 | peer: peer.clone(), 52 | key: box_::precompute(&peer.pk, core.enc_sk()), 53 | peers: peers.clone(), 54 | should_buffer: true, 55 | chat_buf: Default::default(), 56 | tolerate_read_errs: true, 57 | timeout_inactivity: core.set_core_timeout( 58 | Duration::from_secs(INACTIVITY_TIMEOUT_SECS), 59 | CoreTimer::new(token, INACTIVITY_TIMEOUT_ID), 60 | ), 61 | _timeout_tolerate_read_errs: core.set_core_timeout( 62 | Duration::from_secs(TOLERATE_READ_ERRS_SECS), 63 | CoreTimer::new(token, TOLERATE_READ_ERRS_ID), 64 | ), 65 | tx: tx.clone(), 66 | })); 67 | 68 | if let Err((state, e)) = core.insert_peer_state(token, state) { 69 | info!("Could not insert peer-state: {:?}", e); 70 | state.borrow_mut().terminate(core, poll); 71 | unwrap!(tx.send(Event::PeerConnectFailed(peer))); 72 | return; 73 | } 74 | 75 | let mut peers_guard = unwrap!(peers.lock()); 76 | let stored_state = peers_guard 77 | .entry(peer.clone()) 78 | .or_insert(Default::default()); 79 | *stored_state = PeerState::Connected(token); 80 | 81 | unwrap!(tx.send(Event::PeerConnected(peer, token))); 82 | } 83 | 84 | pub fn start_buffering(&mut self) { 85 | self.should_buffer = true; 86 | } 87 | 88 | pub fn flush_and_stop_buffering(&mut self) { 89 | self.should_buffer = false; 90 | for m in self.chat_buf.drain(..) { 91 | println!("{} --> {}", self.peer, m); 92 | } 93 | } 94 | 95 | fn read(&mut self, core: &mut Core, poll: &Poll) { 96 | loop { 97 | match self.sock.read() { 98 | Ok(Some(PeerMsg::CipherText(ct))) => { 99 | if !self.handle_ciphertext(core, poll, &ct) { 100 | return self.terminate(core, poll); 101 | } 102 | } 103 | Ok(Some(_)) => { 104 | debug!("Invalid peer-chat message"); 105 | return self.terminate(core, poll); 106 | } 107 | Ok(None) => return, 108 | Err(e) => { 109 | // TODO Make this debug better as such: 110 | // debug!("{:?} - Failed to read from sock: {:?}", self.our_id, e); 111 | if self.tolerate_read_errs { 112 | trace!("Tolerating read error: {:?}", e); 113 | } else { 114 | debug!("Failed to read from sock: {:?}", e); 115 | return self.terminate(core, poll); 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | fn write(&mut self, core: &mut Core, poll: &Poll, m: Option) { 123 | if let Err(e) = self.sock.write(m.map(|m| (m, 0))) { 124 | debug!("Failed to write to sock: {:?}", e); 125 | self.terminate(core, poll); 126 | } 127 | } 128 | 129 | fn handle_ciphertext(&mut self, core: &mut Core, _poll: &Poll, ciphertext: &[u8]) -> bool { 130 | let plaintext_ser = match msg_to_read(ciphertext, &self.key) { 131 | Ok(pt) => pt, 132 | Err(e) => { 133 | return if self.tolerate_read_errs { 134 | trace!("Tolerating error decrypting: {:?}", e); 135 | true 136 | } else { 137 | debug!("Error decrypting: {:?}", e); 138 | false 139 | }; 140 | } 141 | }; 142 | 143 | let plaintext = match deserialise(&plaintext_ser) { 144 | Ok(pt) => pt, 145 | Err(e) => { 146 | return if self.tolerate_read_errs { 147 | trace!("Tolerating error deserialising: {:?}", e); 148 | true 149 | } else { 150 | info!("Error deserialising: {:?}", e); 151 | false 152 | }; 153 | } 154 | }; 155 | 156 | let chat = match plaintext { 157 | PlainTextMsg::Chat(m) => m, 158 | x => { 159 | return if self.tolerate_read_errs { 160 | trace!("Tolerating invalid PlainTextMsg: {:?}", x); 161 | true 162 | } else { 163 | info!("Invalid PlainTextMsg: {:?}", x); 164 | false 165 | }; 166 | } 167 | }; 168 | 169 | if self.should_buffer { 170 | self.chat_buf.push(chat); 171 | } else { 172 | println!("{} --> {}", self.peer, chat); 173 | } 174 | 175 | let _ = core.cancel_core_timeout(&self.timeout_inactivity); 176 | self.timeout_inactivity = core.set_core_timeout( 177 | Duration::from_secs(INACTIVITY_TIMEOUT_SECS), 178 | CoreTimer::new(self.token, INACTIVITY_TIMEOUT_ID), 179 | ); 180 | 181 | true 182 | } 183 | } 184 | 185 | impl CoreState for ActivePeer { 186 | fn ready(&mut self, core: &mut Core, poll: &Poll, kind: Ready) { 187 | if kind.is_readable() { 188 | self.read(core, poll); 189 | } else if kind.is_writable() { 190 | self.write(core, poll, None) 191 | } else { 192 | warn!("Unknown kind: {:?}", kind); 193 | } 194 | } 195 | 196 | fn write(&mut self, core: &mut Core, poll: &Poll, data: Vec) { 197 | let ciphertext = unwrap!(msg_to_send(&data, &self.key)); 198 | self.write(core, poll, Some(PeerMsg::CipherText(ciphertext))); 199 | } 200 | 201 | fn timeout(&mut self, core: &mut Core, poll: &Poll, timer_id: u8) { 202 | if timer_id == INACTIVITY_TIMEOUT_ID { 203 | trace!( 204 | "Peer inactive for {} secs. Terminating..", 205 | INACTIVITY_TIMEOUT_SECS 206 | ); 207 | return self.terminate(core, poll); 208 | } 209 | 210 | assert_eq!(timer_id, TOLERATE_READ_ERRS_ID); 211 | self.tolerate_read_errs = false; 212 | } 213 | 214 | fn terminate(&mut self, core: &mut Core, poll: &Poll) { 215 | let mut peers_guard = unwrap!(self.peers.lock()); 216 | if let Some(stored_state) = peers_guard.get_mut(&self.peer) { 217 | *stored_state = Default::default(); 218 | } 219 | 220 | let _ = core.remove_peer_state(self.token); 221 | let _ = poll.deregister(&self.sock); 222 | 223 | let _ = self.tx.send(Event::PeerDisconnected(self.peer.clone())); 224 | } 225 | 226 | fn as_any(&mut self) -> &mut Any { 227 | self 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /examples/peer_impl/event.rs: -------------------------------------------------------------------------------- 1 | use common::types::PeerId; 2 | use mio::Token; 3 | use std::fmt; 4 | 5 | #[derive(Debug)] 6 | pub enum Event { 7 | OverlayConnected(Token), 8 | OverlayConnectFailed, 9 | PeersRefreshed, 10 | PeerConnected(PeerId, Token), 11 | PeerConnectFailed(PeerId), 12 | PeerDisconnected(PeerId), 13 | Quit, 14 | } 15 | 16 | impl fmt::Display for Event { 17 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 18 | match self { 19 | Event::OverlayConnected(_) => write!(f, "###### Overlay is connected. ######"), 20 | Event::OverlayConnectFailed => write!(f, "###### Overlay connect failed. ######"), 21 | Event::PeersRefreshed => write!(f, "###### Peer List has been refreshed. ######"), 22 | Event::PeerConnected(ref id, ..) => write!(f, "###### {} is now connected. ######", id), 23 | Event::PeerConnectFailed(ref id) => { 24 | write!(f, "###### {} is could not be connected. ######", id) 25 | } 26 | Event::PeerDisconnected(ref id) => { 27 | write!(f, "###### {} has been disconnected. ######", id) 28 | } 29 | Event::Quit => write!(f, "Event::Quit"), 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/peer_impl/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::active_peer::ActivePeer; 2 | pub use self::event::Event; 3 | pub use self::overlay_connect::OverlayConnect; 4 | 5 | use common::event_loop::{spawn_event_loop, CoreMsg, El}; 6 | use common::read_config; 7 | use common::types::PlainTextMsg; 8 | use maidsafe_utilities::serialisation::serialise; 9 | use maidsafe_utilities::thread::{self, Joiner}; 10 | use mio::Token; 11 | use p2p::{Config, Handle, RendezvousInfo}; 12 | use std::io; 13 | use std::net::SocketAddr; 14 | use std::sync::{mpsc, Arc, Mutex}; 15 | use std::time::Duration; 16 | use std::time::Instant; 17 | 18 | mod active_peer; 19 | mod event; 20 | mod overlay_connect; 21 | 22 | #[derive(Serialize, Deserialize)] 23 | pub struct FullConfig { 24 | pub peer_cfg: PeerConfig, 25 | pub p2p_cfg: Config, 26 | } 27 | 28 | #[derive(Serialize, Deserialize)] 29 | pub struct PeerConfig { 30 | overlay_addr: SocketAddr, 31 | } 32 | 33 | #[derive(Debug)] 34 | pub enum PeerState { 35 | Discovered, 36 | CreatingRendezvousInfo { 37 | mediator_token: Token, 38 | peer_info: Option, 39 | }, 40 | AwaitingPeerRendezvous { 41 | since: Instant, 42 | p2p_handle: Handle, 43 | }, 44 | AwaitingHolePunchResult, 45 | Connected(Token), 46 | } 47 | 48 | impl Default for PeerState { 49 | fn default() -> Self { 50 | PeerState::Discovered 51 | } 52 | } 53 | 54 | const MENU: &str = " 55 | ------------------------------------------- 56 | | ====== 57 | | | Menu | 58 | | ====== 59 | | 1) Show Online & Connected (*) Peers 60 | | 2) Refresh Online Peers List 61 | | 3) Connect to 62 | | 4) Chat with 63 | | 5) Information about inputs to (2) and (3) 64 | | 0) Quit/Exit 65 | ------------------------------------------- 66 | "; 67 | 68 | pub fn entry_point() { 69 | let cfg: FullConfig = read_config("./peer-config"); 70 | 71 | let el = spawn_event_loop(cfg.p2p_cfg); 72 | let peer_cfg = cfg.peer_cfg; 73 | 74 | println!("Enter Name [Name must be unique (preferably) and cannot contain spaces]:"); 75 | 76 | let mut name = String::new(); 77 | loop { 78 | unwrap!(io::stdin().read_line(&mut name)); 79 | name = name.trim().to_string(); 80 | if name.is_empty() || name.contains(" ") { 81 | println!("Invalid Name. Choose a valid Name:"); 82 | name.clear(); 83 | } else { 84 | break; 85 | } 86 | } 87 | 88 | let (event_tx, event_rx) = mpsc::channel(); 89 | let peers = Arc::new(Mutex::new(Default::default())); 90 | 91 | { 92 | let tx = event_tx.clone(); 93 | let name = name.clone(); 94 | let peers = peers.clone(); 95 | let overlay = peer_cfg.overlay_addr; 96 | unwrap!(el.core_tx.send(CoreMsg::new(move |core, poll| { 97 | OverlayConnect::start(core, poll, &overlay, name, peers, tx); 98 | }))); 99 | } 100 | 101 | let event = unwrap!(event_rx.recv_timeout(Duration::from_secs(5))); 102 | println!("{}", event); 103 | let overlay_token = match event { 104 | Event::OverlayConnected(t) => t, 105 | x => panic!("Unexpected event: {:?}", x), 106 | }; 107 | 108 | let _j = print_events(event_rx); 109 | 110 | let mut choice = String::new(); 111 | loop { 112 | println!("\n{}\nChoose an option:", MENU); 113 | unwrap!(io::stdin().read_line(&mut choice)); 114 | choice = choice.trim().to_string(); 115 | 116 | if choice == "1" { 117 | let mut list = String::new(); 118 | unwrap!(peers.lock()) 119 | .iter() 120 | .for_each(|(ref id, ref peer_state)| { 121 | list.push_str(&format!( 122 | "{} {}\n", 123 | id, 124 | if let PeerState::Connected(_) = peer_state { 125 | "*" 126 | } else { 127 | "" 128 | } 129 | )) 130 | }); 131 | if list.is_empty() { 132 | list = "List is empty. Try refreshing.".to_string(); 133 | } 134 | println!("List:\n{}", list); 135 | } else if choice == "2" { 136 | print!("Refreshing... "); 137 | unwrap!(el.core_tx.send(CoreMsg::new(move |core, poll| { 138 | if let Some(overlay) = core.peer_state(overlay_token) { 139 | let m = unwrap!(serialise(&PlainTextMsg::ReqOnlinePeers)); 140 | overlay.borrow_mut().write(core, poll, m); 141 | } 142 | }))); 143 | } else if choice == "3" { 144 | println!("Enter peer id. Partial id/name can be given:"); 145 | 146 | let mut peer_choice = String::new(); 147 | unwrap!(io::stdin().read_line(&mut peer_choice)); 148 | peer_choice = peer_choice.trim().to_string(); 149 | 150 | let mut found_peer = None; 151 | { 152 | let peers_guard = unwrap!(peers.lock()); 153 | for (id, peer_state) in &*peers_guard { 154 | let peer_fmt = format!("{}", id); 155 | if peer_fmt.contains(&peer_choice) { 156 | if let PeerState::Discovered = peer_state { 157 | if found_peer.is_some() { 158 | println!( 159 | "Ambiguous, multiple matches found. More qualification needed." 160 | ); 161 | found_peer = None; 162 | break; 163 | } else { 164 | found_peer = Some(id.clone()); 165 | } 166 | } else { 167 | println!( 168 | "Peer is either in the process of being connected or is already \ 169 | connected. Check status after sometime and retry if necessary." 170 | ); 171 | found_peer = None; 172 | break; 173 | } 174 | } 175 | } 176 | } 177 | 178 | if let Some(peer_id) = found_peer { 179 | unwrap!(el.core_tx.send(CoreMsg::new(move |core, poll| { 180 | let overlay = unwrap!(core.peer_state(overlay_token)); 181 | let mut overlay = overlay.borrow_mut(); 182 | let overlay_connect = 183 | unwrap!(overlay.as_any().downcast_mut::()); 184 | overlay_connect.start_connect_with_peer(core, poll, peer_id.clone()); 185 | }))); 186 | } else { 187 | println!("Aborting due to previous errors (if printed) or due to peer not found."); 188 | } 189 | } else if choice == "4" { 190 | println!("Enter peer id. Partial id/name can be given:"); 191 | 192 | let mut peer_choice = String::new(); 193 | unwrap!(io::stdin().read_line(&mut peer_choice)); 194 | peer_choice = peer_choice.trim().to_string(); 195 | 196 | let mut found_peer = None; 197 | { 198 | let peers_guard = unwrap!(peers.lock()); 199 | for (id, peer_state) in &*peers_guard { 200 | let peer_fmt = format!("{}", id); 201 | if peer_fmt.contains(&peer_choice) { 202 | if let PeerState::Connected(token) = peer_state { 203 | if found_peer.is_some() { 204 | println!( 205 | "Ambiguous, multiple matches found. More qualification needed." 206 | ); 207 | found_peer = None; 208 | break; 209 | } else { 210 | found_peer = Some(*token); 211 | } 212 | } else { 213 | println!( 214 | "Peer is not connected. Check status after sometime and retry if \ 215 | necessary." 216 | ); 217 | found_peer = None; 218 | break; 219 | } 220 | } 221 | } 222 | } 223 | 224 | if let Some(token) = found_peer { 225 | let (tx, rx) = mpsc::channel(); 226 | let tx_clone = tx.clone(); 227 | unwrap!(el.core_tx.send(CoreMsg::new(move |core, _poll| { 228 | let peer = unwrap!(core.peer_state(token)); 229 | let mut peer = peer.borrow_mut(); 230 | let active_peer = unwrap!(peer.as_any().downcast_mut::()); 231 | active_peer.flush_and_stop_buffering(); 232 | 233 | unwrap!(tx_clone.send(())); 234 | }))); 235 | 236 | unwrap!(rx.recv()); 237 | let disconnected = start_chat(&el, token); 238 | 239 | if !disconnected { 240 | unwrap!(el.core_tx.send(CoreMsg::new(move |core, _poll| { 241 | let peer = unwrap!(core.peer_state(token)); 242 | let mut peer = peer.borrow_mut(); 243 | let active_peer = unwrap!(peer.as_any().downcast_mut::()); 244 | active_peer.start_buffering(); 245 | 246 | unwrap!(tx.send(())); 247 | }))); 248 | unwrap!(rx.recv()); 249 | } 250 | } else { 251 | println!("Aborting due to previous errors (if printed) or due to peer not found."); 252 | } 253 | } else if choice == "5" { 254 | println!( 255 | "E.g. if the list has a peer id as \"Blah (1baf3e..)\" you may enter the \ 256 | whole thing as is, or just \"Blah\" or just \"lah\" or just \"af3e\" etc. all \ 257 | without quotes. However if the list also contains someone else like \"Blah-blah\" \ 258 | then \"lah\" will match that too and return error for non unique match. In such \ 259 | cases qualify more until you have a unique match, giving the whole ID in the worst \ 260 | case." 261 | ); 262 | } else if choice == "0" { 263 | break; 264 | } else { 265 | println!("Invalid option !"); 266 | } 267 | 268 | choice.clear(); 269 | } 270 | 271 | unwrap!(event_tx.send(Event::Quit)); 272 | } 273 | 274 | fn start_chat(el: &El, peer: Token) -> bool { 275 | println!("Enter (without quotes) \"\" to exit this chat."); 276 | 277 | let mut disconnected = false; 278 | let (tx, rx) = mpsc::channel(); 279 | loop { 280 | let mut input = String::new(); 281 | let _ = unwrap!(io::stdin().read_line(&mut input)); 282 | input = input.trim().to_owned(); 283 | 284 | if input == "" { 285 | break; 286 | } 287 | 288 | let m = unwrap!(serialise(&PlainTextMsg::Chat(input))); 289 | 290 | let tx = tx.clone(); 291 | unwrap!(el.core_tx.send(CoreMsg::new(move |core, poll| { 292 | if let Some(active_peer) = core.peer_state(peer) { 293 | active_peer.borrow_mut().write(core, poll, m); 294 | unwrap!(tx.send(true)); 295 | } else { 296 | unwrap!(tx.send(false)); 297 | } 298 | }))); 299 | 300 | if !unwrap!(rx.recv()) { 301 | println!("Peer is now disconnected. Try reconnecting to chat again."); 302 | disconnected = true; 303 | break; 304 | } 305 | } 306 | 307 | disconnected 308 | } 309 | 310 | fn print_events(rx: mpsc::Receiver) -> Joiner { 311 | thread::named("Event-Rx", move || { 312 | for event in rx.iter() { 313 | match event { 314 | Event::Quit => break, 315 | e => println!("{}", e), 316 | } 317 | } 318 | }) 319 | } 320 | -------------------------------------------------------------------------------- /examples/peer_impl/overlay_connect.rs: -------------------------------------------------------------------------------- 1 | use common::event_loop::{Core, CoreState, CoreTimer}; 2 | use common::types::{PeerId, PeerMsg, PlainTextMsg}; 3 | use maidsafe_utilities::serialisation::{deserialise, serialise}; 4 | use mio::{Poll, PollOpt, Ready, Token}; 5 | use mio_extras::timer::Timeout; 6 | use p2p::{ 7 | msg_to_read, msg_to_send, Handle, HolePunchInfo, HolePunchMediator, Interface, NatInfo, 8 | QueuedNotifier, RendezvousInfo, Res, 9 | }; 10 | use socket_collection::TcpSock; 11 | use sodium::crypto::box_; 12 | use std::any::Any; 13 | use std::cell::RefCell; 14 | use std::collections::BTreeMap; 15 | use std::net::SocketAddr; 16 | use std::rc::{Rc, Weak}; 17 | use std::sync::mpsc::Sender; 18 | use std::sync::{Arc, Mutex}; 19 | use std::time::{Duration, Instant}; 20 | use std::{fmt, mem}; 21 | use {ActivePeer, Event, PeerState}; 22 | 23 | const PURGE_EXPIRED_AWAITS_SECS: u64 = 60; 24 | const TIMER_ID: u8 = 0; 25 | 26 | pub struct OverlayConnect { 27 | token: Token, 28 | sock: TcpSock, 29 | our_name: String, 30 | state: CurrentState, 31 | peers: Arc>>, 32 | tx: Sender, 33 | timeout: Timeout, 34 | self_weak: Weak>, 35 | } 36 | 37 | enum CurrentState { 38 | Init, 39 | AwaitingOverlayPk, 40 | AwaitingUpdateNameResp { 41 | pk: box_::PublicKey, 42 | key: box_::PrecomputedKey, 43 | }, 44 | OverlayActivated { 45 | _pk: box_::PublicKey, 46 | key: box_::PrecomputedKey, 47 | }, 48 | } 49 | 50 | impl Default for CurrentState { 51 | fn default() -> Self { 52 | CurrentState::Init 53 | } 54 | } 55 | 56 | impl fmt::Debug for CurrentState { 57 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 58 | match *self { 59 | CurrentState::Init => write!(f, "CurrentState::Init"), 60 | CurrentState::AwaitingOverlayPk => write!(f, "CurrentState::AwaitingOverlayPk"), 61 | CurrentState::AwaitingUpdateNameResp { .. } => { 62 | write!(f, "CurrentState::AwaitingUpdateNameResp") 63 | } 64 | CurrentState::OverlayActivated { .. } => write!(f, "CurrentState::OverlayActivated"), 65 | } 66 | } 67 | } 68 | 69 | impl OverlayConnect { 70 | pub fn start( 71 | core: &mut Core, 72 | poll: &Poll, 73 | overlay: &SocketAddr, 74 | our_name: String, 75 | peers: Arc>>, 76 | tx: Sender, 77 | ) { 78 | let token = core.new_token(); 79 | let sock = unwrap!(TcpSock::connect(overlay)); 80 | 81 | unwrap!(poll.register( 82 | &sock, 83 | token, 84 | Ready::readable() | Ready::writable(), 85 | PollOpt::edge(), 86 | )); 87 | 88 | let state = Rc::new(RefCell::new(OverlayConnect { 89 | token, 90 | sock, 91 | our_name, 92 | state: Default::default(), 93 | peers: peers, 94 | tx, 95 | timeout: core.set_core_timeout( 96 | Duration::from_secs(PURGE_EXPIRED_AWAITS_SECS), 97 | CoreTimer::new(token, TIMER_ID), 98 | ), 99 | self_weak: Default::default(), 100 | })); 101 | state.borrow_mut().self_weak = Rc::downgrade(&state); 102 | 103 | if core.insert_peer_state(token, state).is_err() { 104 | panic!("Could not start Overlay !"); 105 | } 106 | } 107 | 108 | pub fn start_connect_with_peer(&mut self, core: &mut Core, poll: &Poll, peer: PeerId) { 109 | let mut peers_guard = unwrap!(self.peers.lock()); 110 | let stored_state = match peers_guard.get_mut(&peer) { 111 | Some(peer_state) => peer_state, 112 | None => { 113 | info!("Peer no longer online for connection."); 114 | return; 115 | } 116 | }; 117 | 118 | if let PeerState::Discovered = *stored_state { 119 | let weak = self.self_weak.clone(); 120 | let handler = move |ifc: &mut Interface, poll: &Poll, (nat_info, res)| { 121 | if let Some(overlay_connect) = weak.upgrade() { 122 | if let Some(core) = ifc.as_any().downcast_mut::() { 123 | overlay_connect 124 | .borrow_mut() 125 | .handle_rendezvous_res(core, poll, peer, nat_info, res); 126 | } else { 127 | warn!("Failed to conver Interface to Core"); 128 | } 129 | } 130 | }; 131 | 132 | let next_state = 133 | match HolePunchMediator::start(core, poll, QueuedNotifier::new(handler)) { 134 | Ok(mediator_token) => PeerState::CreatingRendezvousInfo { 135 | mediator_token, 136 | peer_info: None, 137 | }, 138 | Err(e) => { 139 | info!("Could not initialise p2p mediator: {:?}", e); 140 | return; 141 | } 142 | }; 143 | 144 | *stored_state = next_state; 145 | } else { 146 | info!("Peer already in the process of being connected to."); 147 | } 148 | } 149 | 150 | fn read(&mut self, core: &mut Core, poll: &Poll) { 151 | loop { 152 | match self.sock.read() { 153 | Ok(Some(PeerMsg::PubKey(pk))) => { 154 | if !self.handle_overlay_pk(core, poll, pk) { 155 | return self.terminate(core, poll); 156 | } 157 | } 158 | Ok(Some(PeerMsg::CipherText(ct))) => { 159 | if !self.handle_ciphertext(core, poll, &ct) { 160 | return self.terminate(core, poll); 161 | } 162 | } 163 | Ok(None) => return, 164 | Err(e) => { 165 | // TODO Make this debug better as such: 166 | // debug!("{:?} - Failed to read from sock: {:?}", self.our_id, e); 167 | debug!("Failed to read from sock: {:?}", e); 168 | return self.terminate(core, poll); 169 | } 170 | } 171 | } 172 | } 173 | 174 | fn write(&mut self, core: &mut Core, poll: &Poll, m: Option) { 175 | if let Err(e) = self.sock.write(m.map(|m| (m, 0))) { 176 | debug!("Failed to write to sock: {:?}", e); 177 | self.terminate(core, poll); 178 | } 179 | } 180 | 181 | fn handle_overlay_pk(&mut self, core: &mut Core, poll: &Poll, pk: box_::PublicKey) -> bool { 182 | match self.state { 183 | CurrentState::AwaitingOverlayPk => (), 184 | ref x => { 185 | info!("Message cannot be handled in the current state: {:?}", x); 186 | return false; 187 | } 188 | } 189 | 190 | let key = box_::precompute(&pk, core.enc_sk()); 191 | 192 | let update_name = unwrap!(serialise(&PlainTextMsg::ReqUpdateName( 193 | self.our_name.clone() 194 | ))); 195 | let ciphertext = unwrap!(msg_to_send(&update_name, &key)); 196 | 197 | self.state = CurrentState::AwaitingUpdateNameResp { pk, key }; 198 | 199 | self.write(core, poll, Some(PeerMsg::CipherText(ciphertext))); 200 | 201 | true 202 | } 203 | 204 | fn handle_ciphertext(&mut self, core: &mut Core, poll: &Poll, ciphertext: &[u8]) -> bool { 205 | let plaintext_ser = match self.state { 206 | CurrentState::AwaitingUpdateNameResp { ref key, .. } 207 | | CurrentState::OverlayActivated { ref key, .. } => { 208 | match msg_to_read(ciphertext, key) { 209 | Ok(pt) => pt, 210 | Err(e) => { 211 | info!("Error decrypting: {:?}", e); 212 | return false; 213 | } 214 | } 215 | } 216 | ref x => { 217 | info!("Message cannot be handled in the current state: {:?}", x); 218 | return false; 219 | } 220 | }; 221 | 222 | let plaintext = match deserialise(&plaintext_ser) { 223 | Ok(pt) => pt, 224 | Err(e) => { 225 | info!("Error deserialising: {:?}", e); 226 | return false; 227 | } 228 | }; 229 | 230 | match plaintext { 231 | PlainTextMsg::UpdateNameResp(is_updated) => { 232 | self.handle_update_name_resp(core, poll, is_updated) 233 | } 234 | PlainTextMsg::OnlinePeersResp(online_peers) => { 235 | self.handle_online_peers_resp(core, poll, online_peers) 236 | } 237 | PlainTextMsg::FwdRendezvousInfo { src_info, src_peer } => { 238 | self.handle_peer_rendezvous(core, poll, src_info, src_peer) 239 | } 240 | x => { 241 | info!("Invalid PlainTextMsg: {:?}", x); 242 | return false; 243 | } 244 | } 245 | } 246 | 247 | fn handle_update_name_resp(&mut self, core: &mut Core, poll: &Poll, is_updated: bool) -> bool { 248 | match mem::replace(&mut self.state, Default::default()) { 249 | CurrentState::AwaitingUpdateNameResp { pk, key } => { 250 | if is_updated { 251 | if let Err(e) = self.tx.send(Event::OverlayConnected(self.token)) { 252 | debug!("Error sending event: {:?}", e); 253 | return false; 254 | } 255 | 256 | self.state = CurrentState::OverlayActivated { _pk: pk, key }; 257 | 258 | true 259 | } else { 260 | if let Err(e) = self.tx.send(Event::OverlayConnectFailed) { 261 | debug!("Error sending event: {:?}", e); 262 | } 263 | self.terminate(core, poll); 264 | 265 | false 266 | } 267 | } 268 | x => { 269 | info!("Message cannot be handled in the current state: {:?}", x); 270 | false 271 | } 272 | } 273 | } 274 | 275 | fn handle_online_peers_resp( 276 | &mut self, 277 | core: &mut Core, 278 | poll: &Poll, 279 | online_peers: Vec, 280 | ) -> bool { 281 | { 282 | let mut peers_guard = unwrap!(self.peers.lock()); 283 | let mut current_peers = mem::replace(&mut *peers_guard, Default::default()); 284 | 285 | online_peers.into_iter().for_each(|id| { 286 | let peer_state = current_peers.remove(&id).unwrap_or(Default::default()); 287 | let _ = peers_guard.insert(id, peer_state); 288 | }); 289 | 290 | current_peers 291 | .into_iter() 292 | .for_each(|(id, peer_state)| match peer_state { 293 | PeerState::AwaitingHolePunchResult | PeerState::Connected(_) => { 294 | let _ = peers_guard.insert(id, peer_state); 295 | } 296 | PeerState::CreatingRendezvousInfo { mediator_token, .. } => { 297 | if let Some(state) = core.state(mediator_token) { 298 | state.borrow_mut().terminate(core, poll); 299 | } 300 | } 301 | _ => (), 302 | }); 303 | } 304 | 305 | if let Err(e) = self.tx.send(Event::PeersRefreshed) { 306 | debug!("Error sending event: {:?}", e); 307 | false 308 | } else { 309 | true 310 | } 311 | } 312 | 313 | fn handle_peer_rendezvous( 314 | &mut self, 315 | core: &mut Core, 316 | poll: &Poll, 317 | src_info: RendezvousInfo, 318 | src_peer: PeerId, 319 | ) -> bool { 320 | let mut peers_guard = unwrap!(self.peers.lock()); 321 | 322 | let stored_state = peers_guard 323 | .entry(src_peer.clone()) 324 | .or_insert(Default::default()); 325 | 326 | let prev_peer_state = mem::replace(stored_state, Default::default()); 327 | 328 | let next_state = match prev_peer_state { 329 | PeerState::AwaitingHolePunchResult | PeerState::Connected(_) => { 330 | info!( 331 | "Got connection request from a connected (or being holepunched to) peer: \ 332 | {}", 333 | src_peer 334 | ); 335 | prev_peer_state 336 | } 337 | PeerState::CreatingRendezvousInfo { 338 | mediator_token, 339 | mut peer_info, 340 | } => { 341 | if peer_info.is_some() { 342 | info!( 343 | "Got RendezvousInfo from a peer we already have an info for: {}", 344 | src_peer 345 | ); 346 | } else { 347 | peer_info = Some(src_info); 348 | } 349 | PeerState::CreatingRendezvousInfo { 350 | mediator_token, 351 | peer_info, 352 | } 353 | } 354 | PeerState::AwaitingPeerRendezvous { p2p_handle, .. } => { 355 | let mediator_token = p2p_handle.mediator_token(); 356 | let weak = self.self_weak.clone(); 357 | let handler = move |ifc: &mut Interface, poll: &Poll, res| { 358 | if let Some(overlay_connect) = weak.upgrade() { 359 | if let Some(core) = ifc.as_any().downcast_mut::() { 360 | overlay_connect 361 | .borrow_mut() 362 | .handle_holepunch_res(core, poll, src_peer, res); 363 | } else { 364 | warn!("Failed to conver Interface to Core"); 365 | } 366 | } 367 | }; 368 | Handle::start_hole_punch( 369 | core, 370 | poll, 371 | mediator_token, 372 | src_info, 373 | QueuedNotifier::new(handler), 374 | ); 375 | PeerState::AwaitingHolePunchResult 376 | } 377 | PeerState::Discovered => { 378 | let weak = self.self_weak.clone(); 379 | let handler = move |ifc: &mut Interface, poll: &Poll, (nat_info, res)| { 380 | if let Some(overlay_connect) = weak.upgrade() { 381 | if let Some(core) = ifc.as_any().downcast_mut::() { 382 | overlay_connect 383 | .borrow_mut() 384 | .handle_rendezvous_res(core, poll, src_peer, nat_info, res); 385 | } else { 386 | warn!("Failed to conver Interface to Core"); 387 | } 388 | } 389 | }; 390 | match HolePunchMediator::start(core, poll, QueuedNotifier::new(handler)) { 391 | Ok(mediator_token) => PeerState::CreatingRendezvousInfo { 392 | mediator_token, 393 | peer_info: Some(src_info), 394 | }, 395 | Err(e) => { 396 | debug!("Could not initialise p2p mediator: {:?}", e); 397 | prev_peer_state 398 | } 399 | } 400 | } 401 | }; 402 | 403 | *stored_state = next_state; 404 | 405 | true 406 | } 407 | 408 | fn handle_rendezvous_res( 409 | &mut self, 410 | core: &mut Core, 411 | poll: &Poll, 412 | for_peer: PeerId, 413 | _nat_info: NatInfo, 414 | res: Res<(Handle, RendezvousInfo)>, 415 | ) -> bool { 416 | let (p2p_handle, our_info) = match res { 417 | Ok(r) => r, 418 | Err(_e) => { 419 | debug!("Rendezvous failed for peer: {}", for_peer); 420 | let mut peers_guard = unwrap!(self.peers.lock()); 421 | if let Some(stored_state) = peers_guard.get_mut(&for_peer) { 422 | *stored_state = Default::default(); 423 | } 424 | return true; 425 | } 426 | }; 427 | 428 | let ciphertext = match self.state { 429 | CurrentState::OverlayActivated { ref key, .. } => { 430 | let plaintext_ser = unwrap!(serialise(&PlainTextMsg::ExchgRendezvousInfo { 431 | src_info: our_info, 432 | dst_peer: for_peer.clone() 433 | })); 434 | match msg_to_send(&plaintext_ser, key) { 435 | Ok(ct) => ct, 436 | Err(e) => { 437 | info!("Error encrypting: {:?}", e); 438 | return false; 439 | } 440 | } 441 | } 442 | ref x => { 443 | info!("Message cannot be handled in the current state: {:?}", x); 444 | return false; 445 | } 446 | }; 447 | 448 | { 449 | let mut peers_guard = unwrap!(self.peers.lock()); 450 | let stored_state = match peers_guard.get_mut(&for_peer) { 451 | Some(peer_state) => peer_state, 452 | None => { 453 | trace!( 454 | "Rendezvous created for peer who is no longer online: {}", 455 | for_peer 456 | ); 457 | return true; 458 | } 459 | }; 460 | 461 | let prev_peer_state = mem::replace(stored_state, Default::default()); 462 | let next_state = match prev_peer_state { 463 | PeerState::CreatingRendezvousInfo { peer_info, .. } => { 464 | if let Some(peer_info) = peer_info { 465 | let mediator_token = p2p_handle.mediator_token(); 466 | let weak = self.self_weak.clone(); 467 | let handler = move |ifc: &mut Interface, poll: &Poll, res| { 468 | if let Some(overlay_connect) = weak.upgrade() { 469 | if let Some(core) = ifc.as_any().downcast_mut::() { 470 | overlay_connect 471 | .borrow_mut() 472 | .handle_holepunch_res(core, poll, for_peer, res); 473 | } else { 474 | warn!("Failed to conver Interface to Core"); 475 | } 476 | } 477 | }; 478 | Handle::start_hole_punch( 479 | core, 480 | poll, 481 | mediator_token, 482 | peer_info, 483 | QueuedNotifier::new(handler), 484 | ); 485 | PeerState::AwaitingHolePunchResult 486 | } else { 487 | PeerState::AwaitingPeerRendezvous { 488 | since: Instant::now(), 489 | p2p_handle, 490 | } 491 | } 492 | } 493 | x => { 494 | warn!( 495 | "Logic Error. handle_rendezvous_res() should not get called when at state: \ 496 | {:?}", 497 | x 498 | ); 499 | return true; 500 | } 501 | }; 502 | 503 | *stored_state = next_state; 504 | } 505 | 506 | self.write(core, poll, Some(PeerMsg::CipherText(ciphertext))); 507 | 508 | true 509 | } 510 | 511 | fn handle_holepunch_res( 512 | &mut self, 513 | core: &mut Core, 514 | poll: &Poll, 515 | for_peer: PeerId, 516 | res: Res, 517 | ) { 518 | let holepunch_info = match res { 519 | Ok(info) => info, 520 | Err(e) => { 521 | debug!("Could not holepunch to {}: {:?}", for_peer, e); 522 | let mut peers_guard = unwrap!(self.peers.lock()); 523 | if let Some(stored_state) = peers_guard.get_mut(&for_peer) { 524 | *stored_state = Default::default(); 525 | } 526 | return; 527 | } 528 | }; 529 | 530 | if for_peer.pk != holepunch_info.enc_pk { 531 | info!("Keys mismatch. Unexpected behaviour."); 532 | return; 533 | } 534 | 535 | if holepunch_info.tcp.is_some() { 536 | trace!( 537 | "Successfully Holepunched via TCP. This example however only continues if UDP \ 538 | Holepunch is successful." 539 | ); 540 | } else { 541 | trace!("Could not HolePunch via TCP"); 542 | } 543 | 544 | let udp = match holepunch_info.udp { 545 | Some(udp) => { 546 | trace!("Successfully Holepunched via UDP: {:?}", udp); 547 | udp 548 | } 549 | None => { 550 | debug!( 551 | "Could not HolePunch via UDP. This example however only continues if UDP \ 552 | Holepunch is successful." 553 | ); 554 | return; 555 | } 556 | }; 557 | 558 | ActivePeer::start( 559 | core, 560 | poll, 561 | udp.token, 562 | udp.sock, 563 | for_peer, 564 | self.peers.clone(), 565 | self.tx.clone(), 566 | ); 567 | } 568 | } 569 | 570 | impl CoreState for OverlayConnect { 571 | fn ready(&mut self, core: &mut Core, poll: &Poll, kind: Ready) { 572 | if kind.is_readable() { 573 | self.read(core, poll) 574 | } else if kind.is_writable() { 575 | let m = if let CurrentState::Init = self.state { 576 | self.state = CurrentState::AwaitingOverlayPk; 577 | Some(PeerMsg::PubKey(*core.enc_pk())) 578 | } else { 579 | None 580 | }; 581 | self.write(core, poll, m) 582 | } else { 583 | warn!("Unknown kind: {:?}", kind); 584 | } 585 | } 586 | 587 | fn write(&mut self, core: &mut Core, poll: &Poll, data: Vec) { 588 | let ciphertext = match self.state { 589 | CurrentState::OverlayActivated { ref key, .. } => unwrap!(msg_to_send(&data, key)), 590 | ref x => { 591 | info!("Message cannot be handled in the current state: {:?}", x); 592 | return; 593 | } 594 | }; 595 | self.write(core, poll, Some(PeerMsg::CipherText(ciphertext))); 596 | } 597 | 598 | fn timeout(&mut self, core: &mut Core, _poll: &Poll, timer_id: u8) { 599 | assert_eq!(timer_id, TIMER_ID); 600 | 601 | { 602 | let mut peers_guard = unwrap!(self.peers.lock()); 603 | peers_guard.values_mut().for_each(|peer_state| { 604 | let mut purge = false; 605 | if let PeerState::AwaitingPeerRendezvous { ref since, .. } = peer_state { 606 | purge = since.elapsed() >= Duration::from_secs(PURGE_EXPIRED_AWAITS_SECS); 607 | } 608 | 609 | if purge { 610 | *peer_state = Default::default(); 611 | } 612 | }); 613 | } 614 | 615 | self.timeout = core.set_core_timeout( 616 | Duration::from_secs(PURGE_EXPIRED_AWAITS_SECS), 617 | CoreTimer::new(self.token, TIMER_ID), 618 | ); 619 | } 620 | 621 | fn terminate(&mut self, core: &mut Core, poll: &Poll) { 622 | let _ = poll.deregister(&self.sock); 623 | let _ = core.cancel_core_timeout(&self.timeout); 624 | let _ = core.remove_peer_state(self.token); 625 | } 626 | 627 | fn as_any(&mut self) -> &mut Any { 628 | self 629 | } 630 | } 631 | -------------------------------------------------------------------------------- /examples/server-config: -------------------------------------------------------------------------------- 1 | { 2 | "server_cfg": { 3 | "overlay_port": 20001 4 | }, 5 | "p2p_cfg": { 6 | "rendezvous_timeout_sec": null, 7 | "hole_punch_timeout_sec": null, 8 | "hole_punch_wait_for_other": null, 9 | "udp_rendezvous_port": 20010, 10 | "tcp_rendezvous_port": 20110, 11 | "remote_udp_rendezvous_servers": [], 12 | "remote_tcp_rendezvous_servers": [], 13 | "udp_hole_punchers": [] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/server.rs: -------------------------------------------------------------------------------- 1 | #![allow(deprecated)] 2 | 3 | #[macro_use] 4 | extern crate log; 5 | extern crate maidsafe_utilities; 6 | extern crate mio; 7 | extern crate mio_extras; 8 | extern crate p2p; 9 | extern crate rust_sodium as sodium; 10 | extern crate serde; 11 | extern crate serde_json; 12 | #[macro_use] 13 | extern crate serde_derive; 14 | extern crate socket_collection; 15 | #[macro_use] 16 | extern crate unwrap; 17 | 18 | pub use server_impl::{entry_point, Overlay, Peer}; 19 | 20 | mod common; 21 | mod server_impl; 22 | 23 | fn main() { 24 | unwrap!(maidsafe_utilities::log::init(true)); 25 | entry_point(); 26 | } 27 | -------------------------------------------------------------------------------- /examples/server_impl/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::overlay::Overlay; 2 | pub use self::peer::Peer; 3 | 4 | use common::event_loop::{spawn_event_loop, CoreMsg}; 5 | use common::read_config; 6 | use p2p::{Config, NatMsg, TcpRendezvousServer, UdpRendezvousServer}; 7 | use std::io; 8 | use std::sync::mpsc; 9 | 10 | mod overlay; 11 | mod peer; 12 | 13 | #[derive(Serialize, Deserialize)] 14 | pub struct FullConfig { 15 | pub server_cfg: ServerConfig, 16 | pub p2p_cfg: Config, 17 | } 18 | 19 | #[derive(Serialize, Deserialize)] 20 | pub struct ServerConfig { 21 | overlay_port: u16, 22 | } 23 | 24 | pub fn entry_point() { 25 | let cfg: FullConfig = read_config("./server-config"); 26 | 27 | let el = spawn_event_loop(cfg.p2p_cfg); 28 | let server_cfg = cfg.server_cfg; 29 | 30 | { 31 | let (tx, rx) = mpsc::channel(); 32 | unwrap!(el.nat_tx.send(NatMsg::new(move |ifc, poll| { 33 | let _token_udp = unwrap!(UdpRendezvousServer::start(ifc, poll)); 34 | let _token_tcp = unwrap!(TcpRendezvousServer::start(ifc, poll)); 35 | unwrap!(tx.send(())); 36 | }))); 37 | unwrap!(rx.recv()); 38 | } 39 | 40 | println!("Rendezvous servers started successfully."); 41 | println!("Should this node also be the overlay [y/n] ?"); 42 | 43 | let mut answer = String::new(); 44 | unwrap!(io::stdin().read_line(&mut answer)); 45 | answer = answer.trim().to_string().to_lowercase(); 46 | 47 | if answer == "y" || answer == "yes" { 48 | let (tx, rx) = mpsc::channel(); 49 | unwrap!(el.core_tx.send(CoreMsg::new(move |core, poll| { 50 | Overlay::start(core, poll, server_cfg.overlay_port); 51 | unwrap!(tx.send(())); 52 | }))); 53 | unwrap!(rx.recv()); 54 | println!("Overlay started successfully."); 55 | } 56 | 57 | println!("Everything done. Blocking main thread until user quits."); 58 | let mut quit = String::new(); 59 | loop { 60 | println!("Enter 'q' to quit"); 61 | unwrap!(io::stdin().read_line(&mut quit)); 62 | quit = quit.trim().to_string().to_lowercase(); 63 | if quit == "q" || quit == "quit" { 64 | break; 65 | } 66 | quit.clear(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/server_impl/overlay.rs: -------------------------------------------------------------------------------- 1 | use common::event_loop::{Core, CoreState}; 2 | use common::types::PeerId; 3 | use mio::net::TcpListener; 4 | use mio::{Poll, PollOpt, Ready, Token}; 5 | use p2p::Interface; 6 | use socket_collection::TcpSock; 7 | use std::any::Any; 8 | use std::cell::RefCell; 9 | use std::collections::BTreeMap; 10 | use std::io::ErrorKind; 11 | use std::net::{IpAddr, Ipv4Addr, SocketAddr}; 12 | use std::rc::Rc; 13 | use std::time::Duration; 14 | use Peer; 15 | 16 | pub struct Overlay { 17 | token: Token, 18 | l: TcpListener, 19 | peers: Rc>>, 20 | } 21 | 22 | impl Overlay { 23 | pub fn start(core: &mut Core, poll: &Poll, overlay_port: u16) { 24 | let local_ep = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), overlay_port); 25 | let l = unwrap!(TcpListener::bind(&local_ep)); 26 | 27 | let token = core.new_token(); 28 | 29 | unwrap!(poll.register( 30 | &l, 31 | token, 32 | Ready::readable() | Ready::error() | Ready::hup(), 33 | PollOpt::edge(), 34 | )); 35 | 36 | let state = Self { 37 | token, 38 | l, 39 | peers: Rc::new(RefCell::new(Default::default())), 40 | }; 41 | 42 | if core 43 | .insert_peer_state(token, Rc::new(RefCell::new(state))) 44 | .is_err() 45 | { 46 | panic!("Could not start Overlay !"); 47 | } 48 | } 49 | 50 | fn accept(&self, core: &mut Core, poll: &Poll) { 51 | loop { 52 | match self.l.accept() { 53 | Ok((socket, _)) => { 54 | unwrap!(socket.set_keepalive(Some(Duration::from_secs(10)))); 55 | let peers = Rc::downgrade(&self.peers); 56 | Peer::start(core, poll, TcpSock::wrap(socket), peers); 57 | } 58 | Err(e) => { 59 | if e.kind() != ErrorKind::WouldBlock && e.kind() != ErrorKind::Interrupted { 60 | warn!("Failed to accept new socket: {:?}", e); 61 | } 62 | break; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | impl CoreState for Overlay { 70 | fn ready(&mut self, core: &mut Core, poll: &Poll, kind: Ready) { 71 | if kind.is_error() || kind.is_hup() { 72 | warn!("Overlay errored out"); 73 | self.terminate(core, poll); 74 | } else if kind.is_readable() { 75 | self.accept(core, poll); 76 | } 77 | } 78 | 79 | fn terminate(&mut self, core: &mut Core, poll: &Poll) { 80 | let _ = poll.deregister(&self.l); 81 | let _ = core.remove_peer_state(self.token); 82 | } 83 | 84 | fn as_any(&mut self) -> &mut Any { 85 | self 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /examples/server_impl/peer.rs: -------------------------------------------------------------------------------- 1 | use common::event_loop::{Core, CoreState}; 2 | use common::types::{PeerId, PeerMsg, PlainTextMsg}; 3 | use maidsafe_utilities::serialisation::{deserialise, serialise}; 4 | use mio::{Poll, PollOpt, Ready, Token}; 5 | use p2p::{msg_to_read, msg_to_send, Interface, RendezvousInfo}; 6 | use socket_collection::TcpSock; 7 | use sodium::crypto::box_; 8 | use std::any::Any; 9 | use std::cell::RefCell; 10 | use std::collections::BTreeMap; 11 | use std::rc::{Rc, Weak}; 12 | use std::{fmt, mem}; 13 | 14 | pub struct Peer { 15 | token: Token, 16 | sock: TcpSock, 17 | state: CurrentState, 18 | peers: Weak>>, 19 | } 20 | 21 | enum CurrentState { 22 | AwaitingPeerPk, 23 | AwaitingPeerName { 24 | pk: box_::PublicKey, 25 | key: box_::PrecomputedKey, 26 | }, 27 | PeerActivated { 28 | id: PeerId, 29 | key: box_::PrecomputedKey, 30 | }, 31 | } 32 | 33 | impl Default for CurrentState { 34 | fn default() -> Self { 35 | CurrentState::AwaitingPeerPk 36 | } 37 | } 38 | 39 | impl fmt::Debug for CurrentState { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | match *self { 42 | CurrentState::AwaitingPeerPk => write!(f, "CurrentState::AwaitingPeerPk"), 43 | CurrentState::AwaitingPeerName { .. } => write!(f, "CurrentState::AwaitingPeerName"), 44 | CurrentState::PeerActivated { .. } => write!(f, "CurrentState::PeerActivated"), 45 | } 46 | } 47 | } 48 | 49 | impl Peer { 50 | pub fn start( 51 | core: &mut Core, 52 | poll: &Poll, 53 | sock: TcpSock, 54 | peers: Weak>>, 55 | ) { 56 | let token = core.new_token(); 57 | 58 | unwrap!(poll.register( 59 | &sock, 60 | token, 61 | Ready::readable() | Ready::writable(), 62 | PollOpt::edge(), 63 | )); 64 | 65 | let state = Self { 66 | token, 67 | sock, 68 | state: Default::default(), 69 | peers, 70 | }; 71 | 72 | if core 73 | .insert_peer_state(token, Rc::new(RefCell::new(state))) 74 | .is_err() 75 | { 76 | panic!("Could not start Overlay !"); 77 | } 78 | } 79 | 80 | fn read(&mut self, core: &mut Core, poll: &Poll) { 81 | loop { 82 | match self.sock.read() { 83 | Ok(Some(PeerMsg::PubKey(pk))) => { 84 | if !self.handle_peer_pk(core, poll, pk) { 85 | return self.terminate(core, poll); 86 | } 87 | } 88 | Ok(Some(PeerMsg::CipherText(ct))) => { 89 | if !self.handle_ciphertext(core, poll, &ct) { 90 | return self.terminate(core, poll); 91 | } 92 | } 93 | Ok(None) => return, 94 | Err(e) => { 95 | // TODO Make this debug better as such: 96 | // debug!("{:?} - Failed to read from sock: {:?}", self.our_id, e); 97 | debug!("Failed to read from sock: {:?}", e); 98 | return self.terminate(core, poll); 99 | } 100 | } 101 | } 102 | } 103 | 104 | fn write(&mut self, core: &mut Core, poll: &Poll, m: Option) { 105 | if let Err(e) = self.sock.write(m.map(|m| (m, 0))) { 106 | debug!("Failed to write to sock: {:?}", e); 107 | self.terminate(core, poll); 108 | } 109 | } 110 | 111 | fn handle_peer_pk(&mut self, core: &mut Core, poll: &Poll, pk: box_::PublicKey) -> bool { 112 | match self.state { 113 | CurrentState::AwaitingPeerPk => (), 114 | ref x => { 115 | info!("Message cannot be handled in the current state: {:?}", x); 116 | return false; 117 | } 118 | } 119 | 120 | self.state = CurrentState::AwaitingPeerName { 121 | pk, 122 | key: box_::precompute(&pk, core.enc_sk()), 123 | }; 124 | let overlay_pk = *core.enc_pk(); 125 | 126 | self.write(core, poll, Some(PeerMsg::PubKey(overlay_pk))); 127 | 128 | true 129 | } 130 | 131 | fn handle_ciphertext(&mut self, core: &mut Core, poll: &Poll, ciphertext: &[u8]) -> bool { 132 | let plaintext_ser = match self.state { 133 | CurrentState::AwaitingPeerName { ref key, .. } 134 | | CurrentState::PeerActivated { ref key, .. } => match msg_to_read(ciphertext, key) { 135 | Ok(pt) => pt, 136 | Err(e) => { 137 | info!("Error decrypting: {:?}", e); 138 | return false; 139 | } 140 | }, 141 | ref x => { 142 | info!("Message cannot be handled in the current state: {:?}", x); 143 | return false; 144 | } 145 | }; 146 | 147 | let plaintext = match deserialise(&plaintext_ser) { 148 | Ok(pt) => pt, 149 | Err(e) => { 150 | info!("Error deserialising: {:?}", e); 151 | return false; 152 | } 153 | }; 154 | 155 | match plaintext { 156 | PlainTextMsg::ReqUpdateName(name) => self.handle_update_name(core, poll, name), 157 | PlainTextMsg::ReqOnlinePeers => self.handle_req_online_peers(core, poll), 158 | PlainTextMsg::ExchgRendezvousInfo { src_info, dst_peer } => { 159 | self.forward_rendezvous_impl(core, poll, src_info, dst_peer) 160 | } 161 | x => { 162 | info!("Invalid PlainTextMsg: {:?}", x); 163 | return false; 164 | } 165 | } 166 | } 167 | 168 | fn handle_update_name(&mut self, core: &mut Core, poll: &Poll, name: String) -> bool { 169 | let peers = match self.peers.upgrade() { 170 | Some(peers) => peers, 171 | None => { 172 | warn!("Peer list unexpectedly unavailable !"); 173 | return false; 174 | } 175 | }; 176 | 177 | match mem::replace(&mut self.state, Default::default()) { 178 | CurrentState::AwaitingPeerName { pk, key } => { 179 | let id = PeerId::new(name, pk); 180 | let ciphertext = if id.name.is_empty() 181 | || id.name.contains(" ") 182 | || peers.borrow().contains_key(&id) 183 | { 184 | trace!("Invalid name or identity already taken - choose a different one"); 185 | let resp_ser = unwrap!(serialise(&PlainTextMsg::UpdateNameResp(false))); 186 | let ciphertext = unwrap!(msg_to_send(&resp_ser, &key)); 187 | 188 | self.state = CurrentState::AwaitingPeerName { pk, key }; 189 | 190 | ciphertext 191 | } else { 192 | let resp_ser = unwrap!(serialise(&PlainTextMsg::UpdateNameResp(true))); 193 | let ciphertext = unwrap!(msg_to_send(&resp_ser, &key)); 194 | 195 | self.state = CurrentState::PeerActivated { 196 | id: id.clone(), 197 | key, 198 | }; 199 | if peers.borrow_mut().insert(id, self.token).is_some() { 200 | panic!("Logic Error in updating name: id existed and is now displaced !"); 201 | } 202 | 203 | ciphertext 204 | }; 205 | 206 | self.write(core, poll, Some(PeerMsg::CipherText(ciphertext))); 207 | true 208 | } 209 | x => { 210 | info!("Message cannot be handled in the current state: {:?}", x); 211 | false 212 | } 213 | } 214 | } 215 | 216 | fn handle_req_online_peers(&mut self, core: &mut Core, poll: &Poll) -> bool { 217 | let ciphertext = match self.state { 218 | CurrentState::PeerActivated { ref key, .. } => { 219 | let peers = match self.peers.upgrade() { 220 | Some(peers) => peers, 221 | None => { 222 | warn!("Peer list unexpectedly unavailable !"); 223 | return false; 224 | } 225 | }; 226 | 227 | let peers: Vec = peers.borrow().keys().cloned().collect(); 228 | 229 | let peers_ser = unwrap!(serialise(&PlainTextMsg::OnlinePeersResp(peers))); 230 | unwrap!(msg_to_send(&peers_ser, key)) 231 | } 232 | ref x => { 233 | info!("Message cannot be handled in the current state: {:?}", x); 234 | return false; 235 | } 236 | }; 237 | 238 | self.write(core, poll, Some(PeerMsg::CipherText(ciphertext))); 239 | 240 | true 241 | } 242 | 243 | fn forward_rendezvous_impl( 244 | &mut self, 245 | core: &mut Core, 246 | poll: &Poll, 247 | src_info: RendezvousInfo, 248 | dst_peer: PeerId, 249 | ) -> bool { 250 | let src_peer = match self.state { 251 | CurrentState::PeerActivated { ref id, .. } => id.clone(), 252 | ref x => { 253 | info!("Message cannot be handled in the current state: {:?}", x); 254 | return false; 255 | } 256 | }; 257 | 258 | let peers = match self.peers.upgrade() { 259 | Some(peers) => peers, 260 | None => { 261 | warn!("Peer list unexpectedly unavailable !"); 262 | return false; 263 | } 264 | }; 265 | 266 | let dst_token = match peers.borrow().get(&dst_peer) { 267 | Some(&t) => t, 268 | None => { 269 | trace!("Destination Peer is no longer online."); 270 | return true; 271 | } 272 | }; 273 | 274 | let dst_peer_state = match core.peer_state(dst_token) { 275 | Some(ps) => ps, 276 | None => { 277 | warn!("Destination Peer is online but does not have a peer-state."); 278 | return true; 279 | } 280 | }; 281 | 282 | let fwd_info = PlainTextMsg::FwdRendezvousInfo { src_info, src_peer }; 283 | 284 | let fwd_info_ser = unwrap!(serialise(&fwd_info)); 285 | dst_peer_state.borrow_mut().write(core, poll, fwd_info_ser); 286 | 287 | true 288 | } 289 | } 290 | 291 | impl CoreState for Peer { 292 | fn ready(&mut self, core: &mut Core, poll: &Poll, kind: Ready) { 293 | if kind.is_readable() { 294 | self.read(core, poll) 295 | } else if kind.is_writable() { 296 | self.write(core, poll, None) 297 | } else { 298 | warn!("Unknown kind: {:?}", kind); 299 | } 300 | } 301 | 302 | fn write(&mut self, core: &mut Core, poll: &Poll, data: Vec) { 303 | let ciphertext = match self.state { 304 | CurrentState::PeerActivated { ref key, .. } => unwrap!(msg_to_send(&data, key)), 305 | ref x => { 306 | info!("Message cannot be handled in the current state: {:?}", x); 307 | return; 308 | } 309 | }; 310 | self.write(core, poll, Some(PeerMsg::CipherText(ciphertext))); 311 | } 312 | 313 | fn terminate(&mut self, core: &mut Core, poll: &Poll) { 314 | if let Some(peers) = self.peers.upgrade() { 315 | if let CurrentState::PeerActivated { ref id, .. } = self.state { 316 | let _ = peers.borrow_mut().remove(&id); 317 | } 318 | } 319 | let _ = poll.deregister(&self.sock); 320 | let _ = core.remove_peer_state(self.token); 321 | } 322 | 323 | fn as_any(&mut self) -> &mut Any { 324 | self 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /pre-commit-git-hook: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ ! "$(rustfmt --version)" =~ "0.9.0" ]]; then 4 | echo "Rustfmt version 0.9.0 needed" 5 | exit 1 6 | fi 7 | 8 | cargo fmt -- --write-mode=diff 9 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | //! Various defaults for behaviour configuration 2 | 3 | use std::net::SocketAddr; 4 | 5 | /// Default for udp rendezvous port 6 | pub const UDP_RENDEZVOUS_PORT: u16 = 5483; 7 | /// Default for tcp rendezvous port 8 | pub const TCP_RENDEZVOUS_PORT: u16 = 5484; 9 | /// Default for rendezvous timeout in seconds 10 | pub const RENDEZVOUS_TIMEOUT_SEC: u64 = 8; 11 | /// Default for hole-punch timeout in seconds 12 | pub const HOLE_PUNCH_TIMEOUT_SEC: u64 = 15; 13 | /// Default for if we want to wait for the other, given one of TCP or UDP has hole-punched 14 | pub const HOLE_PUNCH_WAIT_FOR_OTHER: bool = true; 15 | 16 | /// Various configurations with which to proceed with NAT traversal. 17 | /// 18 | /// User can opt to provide this in a file, read from it and pass it when required. For optional 19 | /// fields that are `None`, reasonable defaults will be used. 20 | #[derive(Debug, Clone, Serialize, Deserialize)] 21 | pub struct Config { 22 | /// Rendezvous timeout in seconds 23 | pub rendezvous_timeout_sec: Option, 24 | /// Hole punch timeout in seconds 25 | pub hole_punch_timeout_sec: Option, 26 | /// If we want to wait for the other, given one of TCP or UDP has hole-punched 27 | pub hole_punch_wait_for_other: Option, 28 | /// UDP Rendezvous port. This is the port our UDP Rendezvous server will bind to and listen on. 29 | pub udp_rendezvous_port: Option, 30 | /// TCP Rendezvous port. This is the port our TCP Rendezvous server will bind to and listen on. 31 | pub tcp_rendezvous_port: Option, 32 | /// Remote UDP Rendezvous servers. It is recommended to provide at-least 2 and ideally 3 or 33 | /// more for proper NAT detection or else detection (and consequently prediction) of an 34 | /// `Endpoint Dependent Mapping` (`EDM`) NAT will fail. 35 | pub remote_udp_rendezvous_servers: Vec, 36 | /// Remote TCP Rendezvous servers. It is recommended to provide at-least 2 and ideally 3 for 37 | /// proper NAT detection or else detection (and consequently prediction) of an `Endpoint 38 | /// Dependent Mapping` (`EDM`) NAT will fail. 39 | pub remote_tcp_rendezvous_servers: Vec, 40 | /// Details of all our UDP hole punchers 41 | pub udp_hole_punchers: Vec, 42 | } 43 | 44 | /// Details of each UDP Hole puncher 45 | #[derive(Debug, Clone, Serialize, Deserialize)] 46 | pub struct UdpHolePuncher { 47 | /// Starting value of `TTL`. 48 | pub starting_ttl: u8, 49 | /// Delay before retransmitting with an incremented value of `TTL`. Once it is established the 50 | /// peer is reached this will be bounced back to the platform default so normal communication 51 | /// can ensue. 52 | pub ttl_increment_delay_ms: u64, 53 | } 54 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use bincode; 2 | use socket_collection; 3 | use std::io; 4 | 5 | quick_error! { 6 | /// Nat-traversal's universal error type. 7 | #[derive(Debug)] 8 | pub enum NatError { 9 | /// Io Error 10 | Io(e: io::Error) { 11 | description(e.description()) 12 | display("{}", e) 13 | cause(e) 14 | from() 15 | } 16 | /// Serialization errors 17 | Serialisation(e: bincode::Error) { 18 | description(e.description()) 19 | display("{}", e) 20 | cause(e) 21 | from() 22 | } 23 | /// Failed to decrypt the cipher text 24 | AsymmetricDecipherFailed { 25 | description("Failed to decrypt the cipher-text") 26 | } 27 | /// Payload size is too large 28 | PayloadSizeProhibitive { 29 | description("Payload size is too large") 30 | } 31 | /// Socket Error 32 | SocketError(e: socket_collection::SocketError) { 33 | description(e.description()) 34 | display("{}", e) 35 | from() 36 | } 37 | 38 | // ======================================= 39 | 40 | /// Rendezvous with server failed for both Tcp and Udp - could not obtain our external 41 | /// address 42 | RendezvousFailed { 43 | description("Rendezvous with server failed for both Tcp and Udp - could not obtain our \ 44 | external address") 45 | } 46 | /// Udp Rendezvous with server failed - could not obtain our external address 47 | UdpRendezvousFailed { 48 | description("Udp Rendezvous with server failed - could not obtain our external address") 49 | } 50 | /// Tcp Rendezvous with server failed - could not obtain our external address 51 | TcpRendezvousFailed { 52 | description("Tcp Rendezvous with server failed - could not obtain our external address") 53 | } 54 | 55 | // ======================================= 56 | 57 | /// Booting up Hole Punch Mediator failed 58 | HolePunchMediatorFailedToStart { 59 | description("Booting Hole Punch Mediator failed") 60 | } 61 | /// Booting up Udp Hole Punch Mediator failed 62 | UdpHolePunchMediatorFailedToStart { 63 | description("Booting Udp Hole Punch Mediator Failed") 64 | } 65 | /// Booting up Tdp Hole Punch Mediator failed 66 | TcpHolePunchMediatorFailedToStart { 67 | description("Booting Tdp Hole Punch Mediator Failed") 68 | } 69 | /// Booting up Udp Rendezvous Server failed 70 | UdpRendezvousServerStartFailed { 71 | description("Booting Udp Rendezvous Server Failed") 72 | } 73 | /// Booting up Tcp Rendezvous Server failed 74 | TcpRendezvousServerStartFailed { 75 | description("Booting Tcp Rendezvous Server Failed") 76 | } 77 | /// Booting up Tcp Rendezvous Server failed 78 | TcpRendezvousExchangerStartFailed { 79 | description("Booting Tcp Rendezvous Exchanger Failed") 80 | } 81 | 82 | // ======================================= 83 | 84 | /// Hole punch failed 85 | HolePunchFailed { 86 | description("Hole punch failed") 87 | } 88 | /// Udp Hole punch failed 89 | UdpHolePunchFailed { 90 | description("Udp Hole punch failed") 91 | } 92 | /// Tcp Hole punch failed 93 | TcpHolePunchFailed { 94 | description("Tcp Hole punch failed") 95 | } 96 | 97 | // ======================================= 98 | 99 | /// Timer ID is invalid 100 | InvalidTimerId { 101 | description("Timer ID is invalid") 102 | } 103 | /// Invalid state - the state may already be active or is an operation is not supposed to 104 | /// be permitted for this state 105 | InvalidState { 106 | description("Invalid state - the state may already be active or is an operation is not \ 107 | supposed to be permitted for this state") 108 | } 109 | /// Notifier has expired (possibly already used for notification) 110 | NotifierExpired { 111 | description("Notifier has expired (possibly already used for notification)") 112 | } 113 | /// Socket is not available 114 | UnregisteredSocket { 115 | description("Socket is not available") 116 | } 117 | /// Unknown error 118 | Unknown { 119 | description("Unknown Error in Nat Traversal") 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/queued_notifier.rs: -------------------------------------------------------------------------------- 1 | use mio::{Poll, Token}; 2 | use std::any::Any; 3 | use std::cell::RefCell; 4 | use std::rc::Rc; 5 | use {Interface, NatError, NatMsg, NatState}; 6 | 7 | type Func = Box; 8 | 9 | /// Fire Queued Requests. 10 | /// 11 | /// Since there's always a high chance of multiple borrows of `RefCell` which can lead to runtime 12 | /// panics specially during callback invocation, it's a lot safer to post a queued response 13 | /// instead. This allows all the borrows to end before the next borrow as the control is first 14 | /// returned back to the event loop. 15 | /// 16 | /// Panics can happen while borrowing an already active borrow of the parent or the child - i.e., 17 | /// in either direction. Consider the following (will panic as explained inline): 18 | /// 19 | /// # Examples 20 | /// 21 | /// ```should_panic 22 | /// # use std::rc::{Rc, Weak}; 23 | /// # use std::cell::RefCell; 24 | /// 25 | /// #[derive(Default)] 26 | /// struct Parent { 27 | /// child: Option>>, 28 | /// self_weak: Weak>, 29 | /// } 30 | /// 31 | /// impl Parent { 32 | /// fn new() -> Rc> { 33 | /// let parent = Rc::new(RefCell::new(Parent::default())); 34 | /// let self_weak = Rc::downgrade(&parent); 35 | /// parent.borrow_mut().self_weak = self_weak; 36 | /// 37 | /// parent 38 | /// } 39 | /// 40 | /// fn foo(&mut self) { 41 | /// let self_weak = self.self_weak.clone(); 42 | /// let f = Box::new(move |a, b| { 43 | /// if let Some(parent) = self_weak.upgrade() { 44 | /// // This will panic if cause_panic_for_parent is true 45 | /// parent.borrow_mut().handle(a, b); 46 | /// } 47 | /// }); 48 | /// self.child = Child::new(f); 49 | /// if let Some(ref child) = self.child { 50 | /// child.borrow_mut().foo(); 51 | /// } 52 | /// } 53 | /// 54 | /// fn handle(&mut self, a: String, b: Vec) { 55 | /// // NOTE: Potential cause for panics. If this condition fires then Child might be 56 | /// // attempted to be borrowed again in the the current borrow hasn't ended for it, 57 | /// // as this is getting called because Child called the given callback (hence it's 58 | /// // borrow is still active). 59 | /// if let Some(ref child) = self.child { 60 | /// // This will panic if cause_panic_for_child is true 61 | /// child.borrow_mut().handle(a, b); 62 | /// } 63 | /// } 64 | /// } 65 | /// 66 | /// struct Child { 67 | /// f: Box)>, 68 | /// } 69 | /// 70 | /// impl Child { 71 | /// fn new(f: Box)>) -> Option>> { 72 | /// // NOTE: Potential cause for panics. If this condition fires then Parent will be 73 | /// // attempted to be borrowed again in the callback though the current borrow hasn't 74 | /// // ended for it. 75 | /// let cause_panic_for_parent = true; 76 | /// if cause_panic_for_parent { 77 | /// f(Default::default(), Default::default()); 78 | /// return None; 79 | /// } 80 | /// Some(Rc::new(RefCell::new(Child { f }))) 81 | /// } 82 | /// 83 | /// fn foo(&mut self) { 84 | /// // NOTE: This will eventually land the control in Parent::handle() which might cause 85 | /// // panic as Child is borrowed again (explained in Parent::handle() above). 86 | /// let a = "queued-notifier docs".to_string(); 87 | /// let b = vec![99, 255, 43]; 88 | /// 89 | /// let cause_panic_for_child = true; 90 | /// if cause_panic_for_child { 91 | /// (self.f)(a, b); 92 | /// } 93 | /// } 94 | /// 95 | /// fn handle(&mut self, a: String, b: Vec) { 96 | /// println!("{},\t{:?}", a, b); 97 | /// } 98 | /// } 99 | /// 100 | /// fn main() { 101 | /// let parent = Parent::new(); 102 | /// parent.borrow_mut().foo(); 103 | /// } 104 | /// ``` 105 | /// 106 | /// If all the callbacks were queued instead, the control would have to first return back to the 107 | /// event loop ending all current borrows first. So now we can appreciate `QueuedNotifier` more 108 | /// under these conditions and designs. 109 | /// 110 | /// With `QueuedNotifier` the above code would change to: 111 | /// 112 | /// # Examples 113 | /// 114 | /// ```text 115 | /// impl Parent { 116 | /// // ... 117 | /// 118 | /// fn foo(&mut self, ifc: &mut Interface) { 119 | /// let self_weak = self.self_weak.clone(); 120 | /// let f = move |_ifc, _poll, (a, b)| { 121 | /// if let Some(parent) = self_weak.upgrade() { 122 | /// // This will no longer cause panic 123 | /// parent.borrow_mut().handle(a, b); 124 | /// } 125 | /// }; 126 | /// self.child = Child::new(ifc, QueuedNotifier::new(f)); 127 | /// 128 | /// // ... 129 | /// } 130 | /// 131 | /// // ... 132 | /// } 133 | /// 134 | /// struct Child { 135 | /// qnot: QueuedNotifier<(String, Vec)>, 136 | /// } 137 | /// 138 | /// impl Child { 139 | /// fn new( 140 | /// ifc: &mut Interface, 141 | /// qnot: QueuedNotifier<(String, Vec)> 142 | /// ) -> Option>> { 143 | /// let cause_panic_for_parent = true; 144 | /// if cause_panic_for_parent { 145 | /// qnot.notify(ifc, (Default::default(), Default::default())); 146 | /// return None; 147 | /// } 148 | /// Some(Rc::new(RefCell::new(Child { f }))) 149 | /// } 150 | /// ``` 151 | pub struct QueuedNotifier { 152 | f: Option>, 153 | } 154 | 155 | impl QueuedNotifier 156 | where 157 | UserData: 'static, 158 | { 159 | /// Construct a `QueuedNotifier`. The observer is the given callback. 160 | pub fn new(f: F) -> Self 161 | where 162 | F: FnOnce(&mut Interface, &Poll, UserData) + 'static, 163 | { 164 | let mut invaiant_f = Some(f); 165 | let f = Box::new(move |ifc: &mut Interface, poll: &Poll, user_data| { 166 | let f = unwrap!(invaiant_f.take()); 167 | f(ifc, poll, user_data) 168 | }); 169 | 170 | QueuedNotifier { f: Some(f) } 171 | } 172 | 173 | /// Notify the observer but not immediately; rather in the subsequent runs of the eventloop. 174 | pub fn notify(&mut self, ifc: &mut Interface, user_data: UserData) -> ::Res<()> { 175 | if let Some(f) = self.f.take() { 176 | QueuedNotifierImpl::initiate(ifc, user_data, f); 177 | Ok(()) 178 | } else { 179 | Err(NatError::NotifierExpired) 180 | } 181 | } 182 | 183 | /// Notify the observer but not immediately; rather in the subsequent runs of the eventloop. 184 | /// It'll internally log a warning if any error is encountered instead of returning one. 185 | pub fn notify_or_warn(&mut self, ifc: &mut Interface, user_data: UserData) { 186 | if let Err(e) = self.notify(ifc, user_data) { 187 | warn!("Error notifying: {:?}", e); 188 | } 189 | } 190 | } 191 | 192 | struct QueuedNotifierImpl { 193 | token: Token, 194 | invariant: Option<(UserData, Func)>, 195 | } 196 | 197 | impl QueuedNotifierImpl 198 | where 199 | UserData: 'static, 200 | { 201 | fn initiate(ifc: &mut Interface, user_data: UserData, f: Func) { 202 | let token = ifc.new_token(); 203 | 204 | let state = Rc::new(RefCell::new(QueuedNotifierImpl { 205 | token, 206 | invariant: Some((user_data, f)), 207 | })); 208 | 209 | if let Err((_, e)) = ifc.insert_state(token, state) { 210 | warn!( 211 | "Could not queue the notification. Observer will never be notified: {:?}", 212 | e 213 | ); 214 | return; 215 | } 216 | 217 | let m = NatMsg::new(move |ifc, poll| { 218 | if let Some(notifier) = ifc.state(token) { 219 | notifier.borrow_mut().terminate(ifc, poll); 220 | } 221 | }); 222 | 223 | if let Err(e) = ifc.sender().send(m) { 224 | warn!( 225 | "Error in sending NatMsg. Queued Notification will never be invoked. Observer \ 226 | will never be notified: {:?}", 227 | e 228 | ); 229 | } 230 | } 231 | } 232 | 233 | impl NatState for QueuedNotifierImpl 234 | where 235 | UserData: 'static, 236 | { 237 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 238 | let _ = ifc.remove_state(self.token); 239 | 240 | let (user_data, mut f) = unwrap!(self.invariant.take()); 241 | f(ifc, poll, user_data) 242 | } 243 | 244 | fn as_any(&mut self) -> &mut Any { 245 | self 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/tcp/hole_punch/listener.rs: -------------------------------------------------------------------------------- 1 | use super::puncher::{Finish, Puncher, Via}; 2 | use mio::tcp::TcpListener; 3 | use mio::{Poll, PollOpt, Ready, Token}; 4 | use socket_collection::TcpSock; 5 | use sodium::crypto::box_; 6 | use std::any::Any; 7 | use std::cell::RefCell; 8 | use std::rc::Rc; 9 | use std::time::Instant; 10 | use {Interface, NatError, NatState}; 11 | 12 | pub struct Listener { 13 | token: Token, 14 | listener: TcpListener, 15 | peer_enc_key: box_::PublicKey, 16 | commenced_at: Instant, 17 | f: Option, 18 | } 19 | 20 | impl Listener { 21 | pub fn start( 22 | ifc: &mut Interface, 23 | poll: &Poll, 24 | l: TcpListener, 25 | peer_enc_key: &box_::PublicKey, 26 | f: Finish, 27 | ) -> ::Res { 28 | let token = ifc.new_token(); 29 | 30 | poll.register( 31 | &l, 32 | token, 33 | Ready::readable() | Ready::error() | Ready::hup(), 34 | PollOpt::edge(), 35 | )?; 36 | 37 | let listener = Rc::new(RefCell::new(Listener { 38 | token, 39 | listener: l, 40 | peer_enc_key: *peer_enc_key, 41 | commenced_at: Instant::now(), 42 | f: Some(f), 43 | })); 44 | 45 | if ifc.insert_state(token, listener.clone()).is_err() { 46 | warn!("Unable to start Listener!"); 47 | listener.borrow_mut().terminate(ifc, poll); 48 | Err(NatError::TcpHolePunchFailed) 49 | } else { 50 | Ok(token) 51 | } 52 | } 53 | 54 | fn accept(&mut self, ifc: &mut Interface, poll: &Poll) { 55 | match self.listener.accept() { 56 | Ok((s, _)) => { 57 | self.terminate(ifc, poll); 58 | let f = match self.f.take() { 59 | Some(f) => f, 60 | None => return, 61 | }; 62 | let sock = TcpSock::wrap(s); 63 | let via = Via::Accept(sock, self.token, self.commenced_at); 64 | if let Err(e) = Puncher::start(ifc, poll, via, &self.peer_enc_key, f) { 65 | debug!("Error accepting direct puncher connection: {:?}", e); 66 | } 67 | } 68 | Err(e) => debug!("Failed to accept new socket: {:?}", e), 69 | } 70 | } 71 | 72 | fn handle_err(&mut self, ifc: &mut Interface, poll: &Poll) { 73 | self.terminate(ifc, poll); 74 | if let Some(ref mut f) = self.f { 75 | f(ifc, poll, self.token, Err(NatError::TcpHolePunchFailed)); 76 | } 77 | } 78 | } 79 | 80 | impl NatState for Listener { 81 | fn ready(&mut self, ifc: &mut Interface, poll: &Poll, event: Ready) { 82 | if event.is_error() || event.is_hup() { 83 | let e = match self.listener.take_error() { 84 | Ok(err) => err.map_or(NatError::Unknown, NatError::from), 85 | Err(e) => From::from(e), 86 | }; 87 | warn!("Error in Listener readiness: {:?}", e); 88 | self.handle_err(ifc, poll) 89 | } else if event.is_readable() { 90 | self.accept(ifc, poll) 91 | } else { 92 | trace!("Ignoring unknown event kind: {:?}", event); 93 | } 94 | } 95 | 96 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 97 | let _ = ifc.remove_state(self.token); 98 | let _ = poll.deregister(&self.listener); 99 | } 100 | 101 | fn as_any(&mut self) -> &mut Any { 102 | self 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/tcp/hole_punch/mod.rs: -------------------------------------------------------------------------------- 1 | use self::listener::Listener; 2 | use self::puncher::{Puncher, Via}; 3 | use self::rendezvous_client::TcpRendezvousClient; 4 | use mio::tcp::{TcpListener, TcpStream}; 5 | use mio::Poll; 6 | use mio::Token; 7 | use rand::{self, Rng}; 8 | use socket_collection::TcpSock; 9 | use sodium::crypto::box_; 10 | use std::any::Any; 11 | use std::cell::RefCell; 12 | use std::collections::HashSet; 13 | use std::fmt::{self, Debug, Formatter}; 14 | use std::mem; 15 | use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; 16 | use std::rc::{Rc, Weak}; 17 | use std::time::Duration; 18 | use tcp::new_reusably_bound_tcp_sockets; 19 | use {Interface, NatError, NatState, NatType, TcpHolePunchInfo}; 20 | 21 | mod listener; 22 | mod puncher; 23 | mod rendezvous_client; 24 | 25 | pub type RendezvousFinsih = Box)>; 26 | pub type HolePunchFinsih = Box)>; 27 | 28 | const LISTENER_BACKLOG: i32 = 100; 29 | 30 | enum State { 31 | None, 32 | Rendezvous { 33 | children: HashSet, 34 | info: (SocketAddr, Vec), 35 | f: RendezvousFinsih, 36 | }, 37 | ReadyToHolePunch(SocketAddr), 38 | HolePunching { 39 | children: HashSet, 40 | f: HolePunchFinsih, 41 | }, 42 | } 43 | impl Debug for State { 44 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 45 | match *self { 46 | State::None => write!(f, "State::None"), 47 | State::Rendezvous { .. } => write!(f, "State::Rendezvous"), 48 | State::ReadyToHolePunch(..) => write!(f, "State::ReadyToHolePunch"), 49 | State::HolePunching { .. } => write!(f, "State::HolePunching"), 50 | } 51 | } 52 | } 53 | 54 | pub struct TcpHolePunchMediator { 55 | state: State, 56 | self_weak: Weak>, 57 | } 58 | 59 | impl TcpHolePunchMediator { 60 | pub fn start( 61 | ifc: &mut Interface, 62 | poll: &Poll, 63 | f: RendezvousFinsih, 64 | ) -> ::Res>> { 65 | let mut servers = ifc.config().remote_tcp_rendezvous_servers.clone(); 66 | let num_servers = servers.len(); 67 | 68 | if num_servers == 0 { 69 | return Err(NatError::TcpHolePunchMediatorFailedToStart); 70 | } else if num_servers < 2 { 71 | info!( 72 | "Tcp: Symmetric NAT detection and port prediction will not be possible using \ 73 | less than 2 Rendezvous Servers. Use at-least 2. Recommended is 3." 74 | ); 75 | } else if num_servers > 3 { 76 | let mut rng = rand::thread_rng(); 77 | rng.shuffle(&mut servers); 78 | servers = servers[..3].to_owned(); 79 | } 80 | 81 | let addr_any = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0)); 82 | let (builders, addr) = new_reusably_bound_tcp_sockets(&addr_any, num_servers)?; 83 | 84 | let mediator = Rc::new(RefCell::new(TcpHolePunchMediator { 85 | state: State::None, 86 | self_weak: Weak::new(), 87 | })); 88 | mediator.borrow_mut().self_weak = Rc::downgrade(&mediator); 89 | let weak = mediator.borrow().self_weak.clone(); 90 | 91 | let mut rendezvous_children = HashSet::with_capacity(builders.len()); 92 | 93 | for (builder, server) in builders.iter().zip(servers.iter()) { 94 | let sock = { 95 | let s = builder.to_tcp_stream()?; 96 | TcpSock::wrap(TcpStream::connect_stream(s, server)?) 97 | }; 98 | 99 | let weak_cloned = weak.clone(); 100 | let handler = move |ifc: &mut Interface, poll: &Poll, child, res| { 101 | if let Some(mediator) = weak_cloned.upgrade() { 102 | mediator 103 | .borrow_mut() 104 | .handle_rendezvous(ifc, poll, child, &res); 105 | } 106 | }; 107 | 108 | if let Ok(child) = TcpRendezvousClient::start(ifc, poll, sock, Box::new(handler)) { 109 | let _ = rendezvous_children.insert(child); 110 | } 111 | } 112 | 113 | if rendezvous_children.is_empty() { 114 | Err(NatError::TcpHolePunchMediatorFailedToStart) 115 | } else { 116 | let n = rendezvous_children.len(); 117 | mediator.borrow_mut().state = State::Rendezvous { 118 | children: rendezvous_children, 119 | info: (addr, Vec::with_capacity(n)), 120 | f, 121 | }; 122 | 123 | Ok(mediator) 124 | } 125 | } 126 | 127 | fn handle_rendezvous( 128 | &mut self, 129 | ifc: &mut Interface, 130 | poll: &Poll, 131 | child: Token, 132 | res: &::Res, 133 | ) { 134 | let r = match self.state { 135 | State::Rendezvous { 136 | ref mut children, 137 | ref mut info, 138 | ref mut f, 139 | } => { 140 | let _ = children.remove(&child); 141 | if let Ok(ext_addr) = res { 142 | info.1.push(*ext_addr); 143 | } 144 | if children.is_empty() { 145 | let ext_addrs = mem::replace(&mut info.1, vec![]); 146 | 147 | if ext_addrs.is_empty() { 148 | f( 149 | ifc, 150 | poll, 151 | NatType::Unknown, 152 | Err(NatError::TcpRendezvousFailed), 153 | ); 154 | Err(NatError::TcpRendezvousFailed) 155 | } else { 156 | let mut nat_type = NatType::Unknown; 157 | match TcpHolePunchMediator::port_prediction(ext_addrs, &mut nat_type) { 158 | Ok(ext_addr) => { 159 | f(ifc, poll, nat_type, Ok(ext_addr)); 160 | Ok(Some(info.0)) 161 | } 162 | _ => { 163 | f(ifc, poll, nat_type, Err(NatError::TcpRendezvousFailed)); 164 | Err(NatError::TcpRendezvousFailed) 165 | } 166 | } 167 | } 168 | } else { 169 | Ok(None) 170 | } 171 | } 172 | ref x => { 173 | warn!( 174 | "Logic Error in state book-keeping - Pls report this as a bug. Expected \ 175 | state: State::Rendezvous ;; Found: {:?}", 176 | x 177 | ); 178 | Err(NatError::InvalidState) 179 | } 180 | }; 181 | 182 | match r { 183 | Ok(Some(our_addr)) => self.state = State::ReadyToHolePunch(our_addr), 184 | Ok(None) => (), 185 | Err(e @ NatError::TcpRendezvousFailed) => { 186 | // This is reached only if children is empty. So no chance of borrow violation for 187 | // children in terminate() 188 | debug!("Terminating due to: {:?}", e); 189 | self.terminate(ifc, poll); 190 | } 191 | // Don't call terminate as that can lead to child being borrowed twice 192 | Err(e) => debug!("Ignoring error in handle rendezvous: {:?}", e), 193 | } 194 | } 195 | 196 | fn port_prediction( 197 | mut ext_addrs: Vec, 198 | nat_type: &mut NatType, 199 | ) -> ::Res { 200 | let mut ext_addr = match ext_addrs.pop() { 201 | Some(addr) => addr, 202 | None => return Err(NatError::TcpRendezvousFailed), 203 | }; 204 | 205 | let mut addrs = vec![ext_addr]; 206 | let mut port_prediction_offset = 0i32; 207 | let mut is_err = false; 208 | for addr in ext_addrs { 209 | addrs.push(addr); 210 | 211 | if ext_addr.ip() != addr.ip() { 212 | info!( 213 | "Symmetric NAT with variable IP mapping detected. No logic for Tcp \ 214 | external address prediction for these circumstances!" 215 | ); 216 | *nat_type = NatType::EDMRandomIp(addrs.into_iter().map(|s| s.ip()).collect()); 217 | is_err = true; 218 | break; 219 | } else if port_prediction_offset == 0 { 220 | port_prediction_offset = i32::from(addr.port()) - i32::from(ext_addr.port()); 221 | } else if port_prediction_offset != i32::from(addr.port()) - i32::from(ext_addr.port()) 222 | { 223 | info!( 224 | "Symmetric NAT with non-uniformly changing port mapping detected. No logic \ 225 | for Tcp external address prediction for these circumstances!" 226 | ); 227 | *nat_type = NatType::EDMRandomPort(addrs.into_iter().map(|s| s.port()).collect()); 228 | is_err = true; 229 | break; 230 | } 231 | 232 | ext_addr = addr; 233 | } 234 | 235 | if is_err { 236 | return Err(NatError::TcpRendezvousFailed); 237 | } 238 | 239 | let port = ext_addr.port(); 240 | ext_addr.set_port((i32::from(port) + port_prediction_offset) as u16); 241 | trace!("Our ext addr by Tcp Rendezvous Client: {}", ext_addr); 242 | 243 | *nat_type = if port_prediction_offset == 0 { 244 | NatType::EIM 245 | } else { 246 | NatType::EDM(port_prediction_offset) 247 | }; 248 | 249 | Ok(ext_addr) 250 | } 251 | 252 | pub fn rendezvous_timeout(&mut self, ifc: &mut Interface, poll: &Poll) -> NatError { 253 | debug!("Timeout for TCP Rendezvous"); 254 | let e = match self.state { 255 | State::Rendezvous { .. } => NatError::TcpRendezvousFailed, 256 | _ => NatError::InvalidState, 257 | }; 258 | 259 | match e { 260 | NatError::InvalidState => (), 261 | ref x => { 262 | debug!("Terminating due to: {:?}", x); 263 | self.terminate(ifc, poll); 264 | } 265 | } 266 | 267 | e 268 | } 269 | 270 | pub fn punch_hole( 271 | &mut self, 272 | ifc: &mut Interface, 273 | poll: &Poll, 274 | peer: SocketAddr, 275 | peer_enc_pk: &box_::PublicKey, 276 | f: HolePunchFinsih, 277 | ) -> ::Res<()> { 278 | let our_addr = match self.state { 279 | State::ReadyToHolePunch(our_addr) => our_addr, 280 | ref x => { 281 | debug!("Improper state for this operation: {:?}", x); 282 | return Err(NatError::InvalidState); 283 | } 284 | }; 285 | 286 | let l = new_reusably_bound_tcp_sockets(&our_addr, 1)?.0[0].listen(LISTENER_BACKLOG)?; 287 | let listener = TcpListener::from_listener(l, &our_addr)?; 288 | 289 | let mut children = HashSet::with_capacity(2); 290 | 291 | let weak = self.self_weak.clone(); 292 | let handler = move |ifc: &mut Interface, poll: &Poll, token, res| { 293 | if let Some(mediator) = weak.upgrade() { 294 | mediator 295 | .borrow_mut() 296 | .handle_hole_punch(ifc, poll, token, res); 297 | } 298 | }; 299 | let via = Via::Connect { 300 | our_addr, 301 | peer_addr: peer, 302 | }; 303 | if let Ok(child) = Puncher::start(ifc, poll, via, peer_enc_pk, Box::new(handler)) { 304 | let _ = children.insert(child); 305 | } 306 | 307 | let weak = self.self_weak.clone(); 308 | let handler = move |ifc: &mut Interface, poll: &Poll, token, res| { 309 | if let Some(mediator) = weak.upgrade() { 310 | mediator 311 | .borrow_mut() 312 | .handle_hole_punch(ifc, poll, token, res); 313 | } 314 | }; 315 | if let Ok(child) = Listener::start(ifc, poll, listener, peer_enc_pk, Box::new(handler)) { 316 | let _ = children.insert(child); 317 | } 318 | 319 | if children.is_empty() { 320 | debug!("Failure: Not even one valid child even managed to start hole punching"); 321 | self.terminate(ifc, poll); 322 | return Err(NatError::TcpHolePunchFailed); 323 | } 324 | 325 | self.state = State::HolePunching { children, f }; 326 | 327 | Ok(()) 328 | } 329 | 330 | fn handle_hole_punch( 331 | &mut self, 332 | ifc: &mut Interface, 333 | poll: &Poll, 334 | child: Token, 335 | res: ::Res<(TcpSock, Duration)>, 336 | ) { 337 | let r = match self.state { 338 | State::HolePunching { 339 | ref mut children, 340 | ref mut f, 341 | } => { 342 | let _ = children.remove(&child); 343 | if let Ok((sock, dur)) = res { 344 | f(ifc, poll, Ok(TcpHolePunchInfo::new(sock, child, dur))); 345 | Ok(true) 346 | } else if children.is_empty() { 347 | f(ifc, poll, Err(NatError::TcpHolePunchFailed)); 348 | Err(NatError::TcpHolePunchFailed) 349 | } else { 350 | Ok(false) 351 | } 352 | } 353 | ref x => { 354 | warn!( 355 | "Logic Error in state book-keeping - Pls report this as a bug. Expected \ 356 | state: State::HolePunching ;; Found: {:?}", 357 | x 358 | ); 359 | Err(NatError::InvalidState) 360 | } 361 | }; 362 | 363 | match r { 364 | Ok(true) => self.terminate(ifc, poll), 365 | Ok(false) => (), 366 | Err(e @ NatError::TcpHolePunchFailed) => { 367 | // This is reached only if children is empty, or we have removed the child that 368 | // called us. So no chance of borrow violation for children in terminate() 369 | debug!("Terminating due to: {:?}", e); 370 | self.terminate(ifc, poll); 371 | } 372 | // Don't call terminate as that can lead to child being borrowed twice 373 | Err(e) => debug!("Ignoring error in handle hole-punch: {:?}", e), 374 | } 375 | } 376 | 377 | fn terminate_children(ifc: &mut Interface, poll: &Poll, children: &mut HashSet) { 378 | for child in children.drain() { 379 | let child = match ifc.state(child) { 380 | Some(state) => state, 381 | None => continue, 382 | }; 383 | 384 | child.borrow_mut().terminate(ifc, poll); 385 | } 386 | } 387 | } 388 | 389 | impl NatState for TcpHolePunchMediator { 390 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 391 | match self.state { 392 | State::Rendezvous { 393 | ref mut children, .. 394 | } 395 | | State::HolePunching { 396 | ref mut children, .. 397 | } => { 398 | TcpHolePunchMediator::terminate_children(ifc, poll, children); 399 | } 400 | State::None | State::ReadyToHolePunch(_) => (), 401 | } 402 | } 403 | 404 | fn as_any(&mut self) -> &mut Any { 405 | self 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /src/tcp/hole_punch/puncher.rs: -------------------------------------------------------------------------------- 1 | use mio::tcp::TcpStream; 2 | use mio::{Poll, PollOpt, Ready, Token}; 3 | use mio_extras::timer::Timeout; 4 | use net2::TcpStreamExt; 5 | use socket_collection::TcpSock; 6 | use sodium::crypto::box_; 7 | use std::any::Any; 8 | use std::cell::RefCell; 9 | use std::mem; 10 | use std::net::SocketAddr; 11 | use std::rc::Rc; 12 | use std::time::{Duration, Instant}; 13 | use tcp::new_reusably_bound_tcp_sockets; 14 | use {Interface, NatError, NatState, NatTimer}; 15 | 16 | pub type Finish = Box)>; 17 | 18 | pub enum Via { 19 | Connect { 20 | our_addr: SocketAddr, 21 | peer_addr: SocketAddr, 22 | }, 23 | Accept(TcpSock, Token, Instant), 24 | } 25 | 26 | const TIMER_ID: u8 = 0; 27 | const RE_CONNECT_MS: u64 = 100; 28 | const CHOOSE_CONN: &[u8] = b"Choose this connection"; 29 | 30 | enum ConnectionChooser { 31 | Choose(Option>), 32 | Wait(box_::PrecomputedKey), 33 | } 34 | 35 | pub struct Puncher { 36 | token: Token, 37 | sock: TcpSock, 38 | our_addr: SocketAddr, 39 | peer_addr: SocketAddr, 40 | via_accept: bool, 41 | connection_chooser: ConnectionChooser, 42 | timeout: Option, 43 | commenced_at: Instant, 44 | f: Finish, 45 | } 46 | 47 | impl Puncher { 48 | pub fn start( 49 | ifc: &mut Interface, 50 | poll: &Poll, 51 | via: Via, 52 | peer_enc_pk: &box_::PublicKey, 53 | f: Finish, 54 | ) -> ::Res { 55 | let (sock, token, via_accept, our_addr, peer_addr, commenced_at) = match via { 56 | Via::Accept(sock, t, commenced_at) => { 57 | let our_addr = sock.local_addr()?; 58 | let peer_addr = sock.peer_addr()?; 59 | (sock, t, true, our_addr, peer_addr, commenced_at) 60 | } 61 | Via::Connect { 62 | our_addr, 63 | peer_addr, 64 | } => { 65 | let stream = new_reusably_bound_tcp_sockets(&our_addr, 1)?.0[0].to_tcp_stream()?; 66 | stream.set_linger(Some(Duration::from_secs(0)))?; 67 | let sock = TcpSock::wrap(TcpStream::connect_stream(stream, &peer_addr)?); 68 | ( 69 | sock, 70 | ifc.new_token(), 71 | false, 72 | our_addr, 73 | peer_addr, 74 | Instant::now(), 75 | ) 76 | } 77 | }; 78 | 79 | poll.register( 80 | &sock, 81 | token, 82 | Ready::readable() | Ready::writable(), 83 | PollOpt::edge(), 84 | )?; 85 | 86 | let key = box_::precompute(peer_enc_pk, ifc.enc_sk()); 87 | let chooser = if ifc.enc_pk() > peer_enc_pk { 88 | ConnectionChooser::Choose(Some(::msg_to_send(CHOOSE_CONN, &key)?)) 89 | } else { 90 | ConnectionChooser::Wait(key) 91 | }; 92 | 93 | let puncher = Rc::new(RefCell::new(Puncher { 94 | token, 95 | sock, 96 | our_addr, 97 | peer_addr, 98 | via_accept, 99 | connection_chooser: chooser, 100 | timeout: None, 101 | commenced_at, 102 | f, 103 | })); 104 | 105 | if let Err((nat_state, e)) = ifc.insert_state(token, puncher) { 106 | debug!("Error inserting state: {:?}", e); 107 | nat_state.borrow_mut().terminate(ifc, poll); 108 | return Err(NatError::TcpHolePunchFailed); 109 | } 110 | 111 | Ok(token) 112 | } 113 | 114 | fn read(&mut self, ifc: &mut Interface, poll: &Poll) { 115 | let mut ok = false; 116 | loop { 117 | match self.sock.read::>() { 118 | Ok(Some(cipher_text)) => { 119 | if let ConnectionChooser::Wait(ref key) = self.connection_chooser { 120 | match ::msg_to_read(&cipher_text, key) { 121 | Ok(ref plain_text) if plain_text == &CHOOSE_CONN => ok = true, 122 | _ => { 123 | debug!("Error: Failed to decrypt a connection-choose order"); 124 | ok = false; 125 | break; 126 | } 127 | } 128 | } else { 129 | debug!("Error: A chooser TcpPucher got a choose order"); 130 | ok = false; 131 | break; 132 | } 133 | } 134 | Ok(None) => { 135 | if ok { 136 | break; 137 | } else { 138 | return; 139 | } 140 | } 141 | Err(e) => { 142 | debug!("Tcp Puncher errored out in read: {:?}", e); 143 | ok = false; 144 | break; 145 | } 146 | } 147 | } 148 | 149 | if ok { 150 | self.done(ifc, poll) 151 | } else { 152 | self.handle_err(ifc, poll) 153 | } 154 | } 155 | 156 | fn write(&mut self, ifc: &mut Interface, poll: &Poll, m: Option>) { 157 | match self.sock.write(m.map(|m| (m, 0))) { 158 | Ok(true) => self.done(ifc, poll), 159 | Ok(false) => (), 160 | Err(e) => { 161 | debug!("Tcp Puncher errored out in write: {:?}", e); 162 | self.handle_err(ifc, poll); 163 | } 164 | } 165 | } 166 | 167 | fn done(&mut self, ifc: &mut Interface, poll: &Poll) { 168 | if let Some(t) = self.timeout.take() { 169 | let _ = ifc.cancel_timeout(&t); 170 | } 171 | let _ = ifc.remove_state(self.token); 172 | let sock = mem::replace(&mut self.sock, Default::default()); 173 | let dur = self.commenced_at.elapsed(); 174 | (*self.f)(ifc, poll, self.token, Ok((sock, dur))); 175 | } 176 | 177 | fn handle_err(&mut self, ifc: &mut Interface, poll: &Poll) { 178 | if self.via_accept { 179 | self.terminate(ifc, poll); 180 | (*self.f)(ifc, poll, self.token, Err(NatError::TcpHolePunchFailed)); 181 | } else { 182 | // NOTE: Windows Fix. Edge trigger on error works like Level trigger on Windows and 183 | // instead of not notifying the error again, it actually notifies it immediately going 184 | // into an infinite loop. So deregister the socket immediately. 185 | let _ = poll.deregister(&self.sock); 186 | let _ = mem::replace(&mut self.sock, Default::default()); 187 | 188 | // If read/write both fire one after another and error out for some reason, good idea 189 | // to cancel any set timers before to not have zombie timers around. Although this 190 | // should not happen because we have deregistered the socket above, still do it for 191 | // defensive programming because mio is not clear if `deregister` will prevent the 192 | // already accumulated events from firing or not. Since our state is still `alive` if 193 | // it does fire for the discarded socket, we will reach here. 194 | if let Some(t) = self.timeout.take() { 195 | let _ = ifc.cancel_timeout(&t); 196 | } 197 | self.timeout = Some(ifc.set_timeout( 198 | Duration::from_millis(RE_CONNECT_MS), 199 | NatTimer::new(self.token, TIMER_ID), 200 | )); 201 | } 202 | } 203 | } 204 | 205 | impl NatState for Puncher { 206 | fn ready(&mut self, ifc: &mut Interface, poll: &Poll, event: Ready) { 207 | if event.is_readable() { 208 | self.read(ifc, poll) 209 | } else if event.is_writable() { 210 | if !self.via_accept { 211 | let r = || -> ::Res { 212 | let sock = mem::replace(&mut self.sock, Default::default()); 213 | sock.set_linger(None)?; 214 | Ok(sock) 215 | }(); 216 | 217 | match r { 218 | Ok(s) => self.sock = s, 219 | Err(e) => { 220 | debug!("Terminating due to error: {:?}", e); 221 | self.terminate(ifc, poll); 222 | (*self.f)(ifc, poll, self.token, Err(NatError::TcpHolePunchFailed)); 223 | } 224 | } 225 | } 226 | let m = if let ConnectionChooser::Choose(ref mut m) = self.connection_chooser { 227 | m.take() 228 | } else { 229 | return; 230 | }; 231 | self.write(ifc, poll, m) 232 | } else { 233 | warn!("Investigate: Ignoring unknown event kind: {:?}", event); 234 | } 235 | } 236 | 237 | fn timeout(&mut self, ifc: &mut Interface, poll: &Poll, timer_id: u8) { 238 | if timer_id != TIMER_ID { 239 | debug!("Invalid timer id: {}", timer_id); 240 | } 241 | 242 | let r = || -> ::Res { 243 | let stream = new_reusably_bound_tcp_sockets(&self.our_addr, 1)?.0[0].to_tcp_stream()?; 244 | stream.set_linger(Some(Duration::from_secs(0)))?; 245 | let sock = TcpSock::wrap(TcpStream::connect_stream(stream, &self.peer_addr)?); 246 | poll.register( 247 | &sock, 248 | self.token, 249 | Ready::readable() | Ready::writable(), 250 | PollOpt::edge(), 251 | )?; 252 | Ok(sock) 253 | }(); 254 | 255 | match r { 256 | Ok(s) => self.sock = s, 257 | Err(e) => { 258 | debug!("Aborting connection attempt due to: {:?}", e); 259 | self.terminate(ifc, poll); 260 | (*self.f)(ifc, poll, self.token, Err(NatError::TcpHolePunchFailed)); 261 | } 262 | } 263 | } 264 | 265 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 266 | if let Some(t) = self.timeout.take() { 267 | let _ = ifc.cancel_timeout(&t); 268 | } 269 | let _ = ifc.remove_state(self.token); 270 | let _ = poll.deregister(&self.sock); 271 | } 272 | 273 | fn as_any(&mut self) -> &mut Any { 274 | self 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/tcp/hole_punch/rendezvous_client.rs: -------------------------------------------------------------------------------- 1 | use mio::{Poll, PollOpt, Ready, Token}; 2 | use socket_collection::{self, TcpSock}; 3 | use sodium::crypto::sealedbox; 4 | use std::any::Any; 5 | use std::cell::RefCell; 6 | use std::net::SocketAddr; 7 | use std::rc::Rc; 8 | use std::str::{self, FromStr}; 9 | use tcp::{TcpEchoReq, TcpEchoResp}; 10 | use {Interface, NatError, NatState}; 11 | 12 | pub type Finish = Box)>; 13 | 14 | pub struct TcpRendezvousClient { 15 | token: Token, 16 | sock: TcpSock, 17 | req: Option, 18 | f: Finish, 19 | } 20 | 21 | impl TcpRendezvousClient { 22 | pub fn start(ifc: &mut Interface, poll: &Poll, sock: TcpSock, f: Finish) -> ::Res { 23 | let token = ifc.new_token(); 24 | 25 | poll.register( 26 | &sock, 27 | token, 28 | Ready::readable() | Ready::writable(), 29 | PollOpt::edge(), 30 | )?; 31 | 32 | let client = Rc::new(RefCell::new(TcpRendezvousClient { 33 | token, 34 | sock, 35 | req: Some(TcpEchoReq(ifc.enc_pk().0)), 36 | f, 37 | })); 38 | 39 | if ifc.insert_state(token, client.clone()).is_err() { 40 | debug!("Unable to insert TcpRendezvousClient State!"); 41 | client.borrow_mut().terminate(ifc, poll); 42 | Err(NatError::TcpRendezvousFailed) 43 | } else { 44 | Ok(token) 45 | } 46 | } 47 | 48 | fn read(&mut self, ifc: &mut Interface, poll: &Poll) { 49 | let mut utf8 = Vec::new(); 50 | loop { 51 | match self.sock.read() { 52 | Ok(Some(TcpEchoResp(cipher_text))) => { 53 | match sealedbox::open(&cipher_text, ifc.enc_pk(), ifc.enc_sk()) { 54 | Ok(plain_text) => utf8 = plain_text, 55 | Err(()) => { 56 | debug!("Error: Failed to decrypt TcpIpEchoResp"); 57 | return self.handle_err(ifc, poll); 58 | } 59 | }; 60 | } 61 | Ok(None) => if utf8.is_empty() { 62 | return; 63 | } else { 64 | break; 65 | }, 66 | Err(socket_collection::SocketError::ZeroByteRead) => { 67 | if utf8.is_empty() { 68 | debug!("Error: Connection shutdown without getting TcpIpEchoResp"); 69 | return self.handle_err(ifc, poll); 70 | } else { 71 | break; 72 | } 73 | } 74 | Err(e) => { 75 | debug!("Tcp Rendezvous client errored out in read: {:?}", e); 76 | return self.handle_err(ifc, poll); 77 | } 78 | } 79 | } 80 | 81 | match str::from_utf8(&utf8) { 82 | Ok(our_ext_addr_str) => match SocketAddr::from_str(our_ext_addr_str) { 83 | Ok(addr) => self.done(ifc, poll, addr), 84 | Err(e) => { 85 | debug!( 86 | "Error: TcpIpEchoResp which contained non-parsable address: {:?}", 87 | e 88 | ); 89 | self.handle_err(ifc, poll) 90 | } 91 | }, 92 | Err(e) => { 93 | debug!( 94 | "Error: TcpIpEchoResp which contained non-utf8 address: {:?}", 95 | e 96 | ); 97 | self.handle_err(ifc, poll) 98 | } 99 | } 100 | } 101 | 102 | fn write(&mut self, ifc: &mut Interface, poll: &Poll, m: Option) { 103 | if let Err(e) = self.sock.write(m.map(|m| (m, 0))) { 104 | debug!("Tcp Rendezvous client errored out in write: {:?}", e); 105 | self.handle_err(ifc, poll); 106 | } 107 | } 108 | 109 | fn done(&mut self, ifc: &mut Interface, poll: &Poll, ext_addr: SocketAddr) { 110 | self.terminate(ifc, poll); 111 | (*self.f)(ifc, poll, self.token, Ok(ext_addr)); 112 | } 113 | 114 | fn handle_err(&mut self, ifc: &mut Interface, poll: &Poll) { 115 | self.terminate(ifc, poll); 116 | (*self.f)(ifc, poll, self.token, Err(NatError::TcpRendezvousFailed)); 117 | } 118 | } 119 | 120 | impl NatState for TcpRendezvousClient { 121 | fn ready(&mut self, ifc: &mut Interface, poll: &Poll, event: Ready) { 122 | if event.is_readable() { 123 | self.read(ifc, poll) 124 | } else if event.is_writable() { 125 | let m = self.req.take(); 126 | self.write(ifc, poll, m) 127 | } else { 128 | warn!("Investigate: Ignoring unknown event kind: {:?}", event); 129 | } 130 | } 131 | 132 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 133 | let _ = ifc.remove_state(self.token); 134 | let _ = poll.deregister(&self.sock); 135 | } 136 | 137 | fn as_any(&mut self) -> &mut Any { 138 | self 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/tcp/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::hole_punch::TcpHolePunchMediator; 2 | pub use self::rendezvous_server::TcpRendezvousServer; 3 | 4 | use net2::TcpBuilder; 5 | use sodium::crypto::box_::PUBLICKEYBYTES; 6 | use std::net::{IpAddr, SocketAddr}; 7 | 8 | mod hole_punch; 9 | mod rendezvous_server; 10 | 11 | #[derive(Debug, Serialize, Deserialize)] 12 | struct TcpEchoReq(pub [u8; PUBLICKEYBYTES]); 13 | #[derive(Debug, Serialize, Deserialize)] 14 | struct TcpEchoResp(pub Vec); 15 | 16 | pub fn new_reusably_bound_tcp_sockets( 17 | local_addr: &SocketAddr, 18 | n: usize, 19 | ) -> ::Res<(Vec, SocketAddr)> { 20 | if n < 1 { 21 | return Ok((vec![], *local_addr)); 22 | } 23 | 24 | let mut v = Vec::with_capacity(n); 25 | 26 | let sock = match local_addr.ip() { 27 | IpAddr::V4(..) => TcpBuilder::new_v4()?, 28 | IpAddr::V6(..) => TcpBuilder::new_v6()?, 29 | }; 30 | let _ = sock.reuse_address(true)?; 31 | enable_so_reuseport(&sock)?; 32 | let _ = sock.bind(local_addr)?; 33 | 34 | let addr = sock.local_addr()?; 35 | 36 | v.push(sock); 37 | 38 | for _ in 0..(n - 1) { 39 | let sock = match local_addr.ip() { 40 | IpAddr::V4(..) => TcpBuilder::new_v4()?, 41 | IpAddr::V6(..) => TcpBuilder::new_v6()?, 42 | }; 43 | let _ = sock.reuse_address(true)?; 44 | enable_so_reuseport(&sock)?; 45 | let _ = sock.bind(&addr)?; 46 | v.push(sock); 47 | } 48 | 49 | Ok((v, addr)) 50 | } 51 | 52 | #[cfg(target_family = "unix")] 53 | fn enable_so_reuseport(sock: &TcpBuilder) -> ::Res<()> { 54 | use net2::unix::UnixTcpBuilderExt; 55 | let _ = sock.reuse_port(true)?; 56 | Ok(()) 57 | } 58 | 59 | #[cfg(target_family = "windows")] 60 | fn enable_so_reuseport(_sock: &TcpBuilder) -> ::Res<()> { 61 | Ok(()) 62 | } 63 | -------------------------------------------------------------------------------- /src/tcp/rendezvous_server/exchange_msg.rs: -------------------------------------------------------------------------------- 1 | use super::{TcpEchoReq, TcpEchoResp}; 2 | use mio::{Poll, PollOpt, Ready, Token}; 3 | use mio_extras::timer::Timeout; 4 | use socket_collection::{SocketError, TcpSock}; 5 | use sodium::crypto::box_; 6 | use sodium::crypto::sealedbox; 7 | use std::any::Any; 8 | use std::cell::RefCell; 9 | use std::net::SocketAddr; 10 | use std::rc::Rc; 11 | use std::time::Duration; 12 | use {Interface, NatError, NatState, NatTimer}; 13 | 14 | const TIMER_ID: u8 = 0; 15 | const RENDEZVOUS_EXCHG_TIMEOUT_SEC: u64 = 5; 16 | 17 | pub struct ExchangeMsg { 18 | token: Token, 19 | sock: TcpSock, 20 | peer: SocketAddr, 21 | timeout: Timeout, 22 | } 23 | 24 | impl ExchangeMsg { 25 | pub fn start(ifc: &mut Interface, poll: &Poll, peer: SocketAddr, sock: TcpSock) -> ::Res<()> { 26 | let token = ifc.new_token(); 27 | 28 | let timeout = ifc.set_timeout( 29 | Duration::from_secs(RENDEZVOUS_EXCHG_TIMEOUT_SEC), 30 | NatTimer::new(token, TIMER_ID), 31 | ); 32 | 33 | poll.register( 34 | &sock, 35 | token, 36 | Ready::readable() | Ready::writable(), 37 | PollOpt::edge(), 38 | )?; 39 | 40 | let exchg_msg = Rc::new(RefCell::new(ExchangeMsg { 41 | token, 42 | sock, 43 | peer, 44 | timeout, 45 | })); 46 | 47 | if ifc.insert_state(token, exchg_msg.clone()).is_err() { 48 | debug!("Unable to start TCP rendezvous exchanger!"); 49 | exchg_msg.borrow_mut().terminate(ifc, poll); 50 | Err(NatError::TcpRendezvousExchangerStartFailed) 51 | } else { 52 | Ok(()) 53 | } 54 | } 55 | 56 | fn read(&mut self, ifc: &mut Interface, poll: &Poll) { 57 | let mut pk = None; 58 | loop { 59 | match self.sock.read() { 60 | Ok(Some(TcpEchoReq(raw))) => pk = Some(box_::PublicKey(raw)), 61 | Ok(None) => if pk.is_some() { 62 | break; 63 | } else { 64 | return; 65 | }, 66 | Err(e) => { 67 | match e { 68 | SocketError::ZeroByteRead => (), // Expected of a well behave client 69 | _ => debug!("Error in read: {:?}", e), 70 | } 71 | return self.terminate(ifc, poll); 72 | } 73 | } 74 | } 75 | 76 | if let Some(pk) = pk.take() { 77 | let resp = TcpEchoResp(sealedbox::seal(format!("{}", self.peer).as_bytes(), &pk)); 78 | self.write(ifc, poll, Some(resp)) 79 | } else { 80 | warn!("Error: Logic error in Tcp Rendezvous Server - Please report."); 81 | return self.terminate(ifc, poll); 82 | } 83 | } 84 | 85 | fn write(&mut self, ifc: &mut Interface, poll: &Poll, m: Option) { 86 | match self.sock.write(m.map(|m| (m, 0))) { 87 | Ok(true) => (), 88 | Ok(false) => return, 89 | Err(e) => { 90 | debug!("Error in write for tcp exchanger: {:?}", e); 91 | self.terminate(ifc, poll); 92 | } 93 | } 94 | } 95 | } 96 | 97 | impl NatState for ExchangeMsg { 98 | fn ready(&mut self, ifc: &mut Interface, poll: &Poll, event: Ready) { 99 | if event.is_readable() { 100 | self.read(ifc, poll) 101 | } else if event.is_writable() { 102 | self.write(ifc, poll, None) 103 | } else { 104 | warn!("Investigate: Ignoring unknown event kind: {:?}", event); 105 | } 106 | } 107 | 108 | fn timeout(&mut self, ifc: &mut Interface, poll: &Poll, timer_id: u8) { 109 | if timer_id != TIMER_ID { 110 | debug!("Invalid Timer ID: {}", timer_id); 111 | } 112 | debug!("Timeout in tcp rendezvous exchanger. Terminating session."); 113 | self.terminate(ifc, poll) 114 | } 115 | 116 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 117 | let _ = ifc.cancel_timeout(&self.timeout); 118 | let _ = ifc.remove_state(self.token); 119 | let _ = poll.deregister(&self.sock); 120 | } 121 | 122 | fn as_any(&mut self) -> &mut Any { 123 | self 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/tcp/rendezvous_server/mod.rs: -------------------------------------------------------------------------------- 1 | use self::exchange_msg::ExchangeMsg; 2 | use config::TCP_RENDEZVOUS_PORT; 3 | use mio::tcp::TcpListener; 4 | use mio::{Poll, PollOpt, Ready, Token}; 5 | use net2::TcpBuilder; 6 | use socket_collection::TcpSock; 7 | use std::any::Any; 8 | use std::cell::RefCell; 9 | use std::io::ErrorKind; 10 | use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; 11 | use std::rc::Rc; 12 | use tcp::{TcpEchoReq, TcpEchoResp}; 13 | use {Interface, NatError, NatState}; 14 | 15 | mod exchange_msg; 16 | 17 | const LISTENER_BACKLOG: i32 = 100; 18 | 19 | /// TCP Rendezvous server. 20 | /// 21 | /// This is intended to be kept running on some publicly reachable endpoint so that peers can 22 | /// obtain their rendezvous information. The information provided back to the peer is encrypted 23 | /// with peer-supplied asymmetric key. Certain router and firewalls scan the packet and if they 24 | /// find an IP address belonging to their pool that they use to do the NAT mapping/translation, 25 | /// they take it as a STUN attempt or similar and mangle the information or even discard it. 26 | /// Encrypting makes sure no such thing happens. 27 | pub struct TcpRendezvousServer { 28 | token: Token, 29 | listener: TcpListener, 30 | } 31 | 32 | impl TcpRendezvousServer { 33 | /// Boot the TCP Rendezvous server. This should normally be called only once. 34 | pub fn start(ifc: &mut Interface, poll: &Poll) -> ::Res { 35 | let listener = { 36 | let port = ifc 37 | .config() 38 | .tcp_rendezvous_port 39 | .unwrap_or(TCP_RENDEZVOUS_PORT); 40 | let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port)); 41 | let builder = TcpBuilder::new_v4()?; 42 | let _ = builder.bind(addr)?; 43 | let l = builder.listen(LISTENER_BACKLOG)?; 44 | TcpListener::from_listener(l, &addr)? 45 | }; 46 | 47 | let token = ifc.new_token(); 48 | 49 | poll.register( 50 | &listener, 51 | token, 52 | Ready::readable() | Ready::error() | Ready::hup(), 53 | PollOpt::edge(), 54 | )?; 55 | 56 | let server = Rc::new(RefCell::new(TcpRendezvousServer { token, listener })); 57 | 58 | if ifc.insert_state(token, server.clone()).is_err() { 59 | warn!("Unable to start TcpRendezvousServer!"); 60 | server.borrow_mut().terminate(ifc, poll); 61 | Err(NatError::TcpRendezvousServerStartFailed) 62 | } else { 63 | Ok(token) 64 | } 65 | } 66 | 67 | fn accept(&mut self, ifc: &mut Interface, poll: &Poll) { 68 | loop { 69 | match self.listener.accept() { 70 | Ok((socket, peer)) => { 71 | if let Err(e) = ExchangeMsg::start(ifc, poll, peer, TcpSock::wrap(socket)) { 72 | debug!("Error accepting direct connection: {:?}", e); 73 | } 74 | } 75 | Err(ref e) 76 | if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::Interrupted => 77 | { 78 | return; 79 | } 80 | Err(e) => { 81 | debug!("Failed to accept new socket: {:?}", e); 82 | return; 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | impl NatState for TcpRendezvousServer { 90 | fn ready(&mut self, ifc: &mut Interface, poll: &Poll, event: Ready) { 91 | if event.is_error() || event.is_hup() { 92 | let e = match self.listener.take_error() { 93 | Ok(err) => err.map_or(NatError::Unknown, NatError::from), 94 | Err(e) => From::from(e), 95 | }; 96 | warn!("Error in TcpRendezvousServer readiness: {:?}", e); 97 | self.terminate(ifc, poll) 98 | } else if event.is_readable() { 99 | self.accept(ifc, poll) 100 | } else { 101 | trace!("Ignoring unknown event kind: {:?}", event); 102 | } 103 | } 104 | 105 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 106 | let _ = ifc.remove_state(self.token); 107 | let _ = poll.deregister(&self.listener); 108 | } 109 | 110 | fn as_any(&mut self) -> &mut Any { 111 | self 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/test_utils.rs: -------------------------------------------------------------------------------- 1 | use super::{Config, Interface, NatMsg, NatState, NatTimer}; 2 | use maidsafe_utilities::thread::{self, Joiner}; 3 | use mio::{Events, Poll, PollOpt, Ready, Token}; 4 | use mio_extras::channel; 5 | use mio_extras::channel::Sender; 6 | use mio_extras::timer::{Timeout, Timer}; 7 | use serde_json; 8 | use sodium::crypto::box_; 9 | use std::any::Any; 10 | use std::cell::RefCell; 11 | use std::collections::hash_map::Entry; 12 | use std::collections::HashMap; 13 | use std::fs::File; 14 | use std::io::Read; 15 | use std::rc::Rc; 16 | use std::time::Duration; 17 | 18 | /// Simplified state machine implementation for tests. 19 | pub struct StateMachine { 20 | pub nat_states: HashMap>>, 21 | pub timer: Timer, 22 | pub token: usize, 23 | pub config: Config, 24 | pub enc_pk: box_::PublicKey, 25 | pub enc_sk: box_::SecretKey, 26 | pub tx: Sender, 27 | } 28 | 29 | impl StateMachine { 30 | pub fn handle_nat_timer(&mut self, poll: &Poll) { 31 | while let Some(nat_timer) = self.timer.poll() { 32 | if let Some(nat_state) = self.state(nat_timer.associated_nat_state) { 33 | nat_state 34 | .borrow_mut() 35 | .timeout(self, poll, nat_timer.timer_id); 36 | } 37 | } 38 | } 39 | 40 | pub fn handle_readiness(&mut self, poll: &Poll, token: Token, kind: Ready) { 41 | if let Some(nat_state) = self.state(token) { 42 | return nat_state.borrow_mut().ready(self, poll, kind); 43 | } 44 | } 45 | } 46 | 47 | impl Interface for StateMachine { 48 | fn insert_state( 49 | &mut self, 50 | token: Token, 51 | state: Rc>, 52 | ) -> Result<(), (Rc>, String)> { 53 | if let Entry::Vacant(ve) = self.nat_states.entry(token) { 54 | let _ = ve.insert(state); 55 | Ok(()) 56 | } else { 57 | Err((state, "Token is already mapped".to_string())) 58 | } 59 | } 60 | 61 | fn remove_state(&mut self, token: Token) -> Option>> { 62 | self.nat_states.remove(&token) 63 | } 64 | 65 | fn state(&mut self, token: Token) -> Option>> { 66 | self.nat_states.get(&token).cloned() 67 | } 68 | 69 | fn set_timeout(&mut self, duration: Duration, timer_detail: NatTimer) -> Timeout { 70 | self.timer.set_timeout(duration, timer_detail) 71 | } 72 | 73 | fn cancel_timeout(&mut self, timeout: &Timeout) -> Option { 74 | self.timer.cancel_timeout(timeout) 75 | } 76 | 77 | fn new_token(&mut self) -> Token { 78 | self.token += 1; 79 | Token(self.token) 80 | } 81 | 82 | fn config(&self) -> &Config { 83 | &self.config 84 | } 85 | 86 | fn enc_pk(&self) -> &box_::PublicKey { 87 | &self.enc_pk 88 | } 89 | 90 | fn enc_sk(&self) -> &box_::SecretKey { 91 | &self.enc_sk 92 | } 93 | 94 | fn sender(&self) -> &Sender { 95 | &self.tx 96 | } 97 | 98 | fn as_any(&mut self) -> &mut Any { 99 | self 100 | } 101 | } 102 | 103 | /// Spawn testing event loop in a separate thread and return a handle to control it. 104 | pub fn spawn_event_loop(config: Config) -> EventLoop { 105 | let (done_tx, done_rx) = channel::channel(); 106 | let (nat_tx, nat_rx) = channel::channel(); 107 | let nat_tx2 = nat_tx.clone(); 108 | 109 | let j = thread::named("Event-Loop", move || { 110 | const TIMER_TOKEN: usize = 0; 111 | const DONE_RX_TOKEN: usize = TIMER_TOKEN + 1; 112 | const NAT_RX_TOKEN: usize = DONE_RX_TOKEN + 1; 113 | 114 | let poll = unwrap!(Poll::new()); 115 | 116 | let (enc_pk, enc_sk) = box_::gen_keypair(); 117 | let timer = Timer::default(); 118 | 119 | unwrap!(poll.register( 120 | &timer, 121 | Token(TIMER_TOKEN), 122 | Ready::readable(), 123 | PollOpt::edge(), 124 | )); 125 | unwrap!(poll.register( 126 | &done_rx, 127 | Token(DONE_RX_TOKEN), 128 | Ready::readable(), 129 | PollOpt::edge(), 130 | )); 131 | unwrap!(poll.register( 132 | &nat_rx, 133 | Token(NAT_RX_TOKEN), 134 | Ready::readable(), 135 | PollOpt::edge(), 136 | )); 137 | 138 | let mut sm = StateMachine { 139 | nat_states: HashMap::with_capacity(10), 140 | timer, 141 | token: NAT_RX_TOKEN + 1, 142 | config, 143 | enc_pk, 144 | enc_sk, 145 | tx: nat_tx, 146 | }; 147 | 148 | let mut events = Events::with_capacity(1024); 149 | 150 | 'event_loop: loop { 151 | unwrap!(poll.poll(&mut events, None)); 152 | 153 | for event in events.iter() { 154 | match event.token() { 155 | Token(TIMER_TOKEN) => { 156 | assert!(event.readiness().is_readable()); 157 | sm.handle_nat_timer(&poll); 158 | } 159 | Token(DONE_RX_TOKEN) => { 160 | assert!(event.readiness().is_readable()); 161 | unwrap!(done_rx.try_recv()); 162 | break 'event_loop; 163 | } 164 | Token(NAT_RX_TOKEN) => { 165 | assert!(event.readiness().is_readable()); 166 | while let Ok(f) = nat_rx.try_recv() { 167 | f.invoke(&mut sm, &poll); 168 | } 169 | } 170 | t => sm.handle_readiness(&poll, t, event.readiness()), 171 | } 172 | } 173 | } 174 | }); 175 | 176 | EventLoop { 177 | nat_tx: nat_tx2, 178 | done_tx, 179 | _j: j, 180 | } 181 | } 182 | 183 | /// Handle to event loop running in a separate thread. 184 | pub struct EventLoop { 185 | pub nat_tx: Sender, 186 | pub done_tx: Sender<()>, 187 | _j: Joiner, 188 | } 189 | 190 | impl Drop for EventLoop { 191 | fn drop(&mut self) { 192 | unwrap!(self.done_tx.send(())); 193 | } 194 | } 195 | 196 | /// Read p2p config from json file. 197 | #[allow(unused)] 198 | pub fn read_config(path: &str) -> Config { 199 | let mut file = unwrap!(File::open(path)); 200 | let mut content = String::new(); 201 | unwrap!(file.read_to_string(&mut content)); 202 | unwrap!(serde_json::from_str(&content)) 203 | } 204 | -------------------------------------------------------------------------------- /src/udp/hole_punch/mod.rs: -------------------------------------------------------------------------------- 1 | use self::puncher::Puncher; 2 | pub use self::rendezvous_client::UdpRendezvousClient; 3 | use mio::Poll; 4 | use mio::Token; 5 | use socket_collection::UdpSock; 6 | use sodium::crypto::box_; 7 | use std::any::Any; 8 | use std::cell::RefCell; 9 | use std::collections::HashSet; 10 | use std::fmt::{self, Debug, Formatter}; 11 | use std::mem; 12 | use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; 13 | use std::rc::{Rc, Weak}; 14 | use std::time::Duration; 15 | use {Interface, NatError, NatState, NatType, UdpHolePunchInfo}; 16 | 17 | mod puncher; 18 | mod rendezvous_client; 19 | 20 | pub type RendezvousFinsih = Box>)>; 21 | pub type HolePunchFinsih = Box)>; 22 | 23 | enum State { 24 | None, 25 | Rendezvous { 26 | children: HashSet, 27 | info: (Vec<(UdpSock, Token)>, Vec), 28 | nat_type: NatType, 29 | f: RendezvousFinsih, 30 | }, 31 | ReadyToHolePunch(Vec<(UdpSock, Token)>), 32 | HolePunching { 33 | children: HashSet, 34 | f: HolePunchFinsih, 35 | }, 36 | } 37 | impl Debug for State { 38 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 39 | match *self { 40 | State::None => write!(f, "State::None"), 41 | State::Rendezvous { .. } => write!(f, "State::Rendezvous"), 42 | State::ReadyToHolePunch(..) => write!(f, "State::ReadyToHolePunch"), 43 | State::HolePunching { .. } => write!(f, "State::HolePunching"), 44 | } 45 | } 46 | } 47 | 48 | pub struct UdpHolePunchMediator { 49 | state: State, 50 | self_weak: Weak>, 51 | } 52 | 53 | impl UdpHolePunchMediator { 54 | pub fn start( 55 | ifc: &mut Interface, 56 | poll: &Poll, 57 | f: RendezvousFinsih, 58 | ) -> ::Res>> { 59 | let mut socks = Vec::with_capacity(ifc.config().udp_hole_punchers.len()); 60 | let addr_any = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0)); 61 | for _ in 0..ifc.config().udp_hole_punchers.len() { 62 | socks.push(UdpSock::bind(&addr_any)?); 63 | } 64 | 65 | let mediator = Rc::new(RefCell::new(UdpHolePunchMediator { 66 | state: State::None, 67 | self_weak: Weak::new(), 68 | })); 69 | mediator.borrow_mut().self_weak = Rc::downgrade(&mediator); 70 | let weak = mediator.borrow().self_weak.clone(); 71 | 72 | let mut rendezvous_children = HashSet::with_capacity(socks.len()); 73 | 74 | for sock in socks { 75 | let weak_cloned = weak.clone(); 76 | let handler = move |ifc: &mut Interface, poll: &Poll, child, nat_type, res| { 77 | if let Some(mediator) = weak_cloned.upgrade() { 78 | mediator 79 | .borrow_mut() 80 | .handle_rendezvous(ifc, poll, child, nat_type, res); 81 | } 82 | }; 83 | 84 | if let Ok(child) = UdpRendezvousClient::start(ifc, poll, sock, Box::new(handler)) { 85 | let _ = rendezvous_children.insert(child); 86 | } 87 | } 88 | 89 | if rendezvous_children.is_empty() { 90 | Err(NatError::UdpHolePunchMediatorFailedToStart) 91 | } else { 92 | let n = rendezvous_children.len(); 93 | mediator.borrow_mut().state = State::Rendezvous { 94 | children: rendezvous_children, 95 | info: (Vec::with_capacity(n), Vec::with_capacity(n)), 96 | nat_type: Default::default(), 97 | f, 98 | }; 99 | 100 | Ok(mediator) 101 | } 102 | } 103 | 104 | // The NatType report will only be considered for the 1st successful child to report a known NAT 105 | // type. This assumes that router does not appear as NatType::EDM to one and NatType::EIM to 106 | // another etc. Just one caveat though: It can appear as EDM to one and 107 | // EDMRandomIp/EDMRandomPort to another if the port at delta/port_prediction_offset happens to 108 | // be occupied by some other socket. We don't want to make the code bloat to handle this rare 109 | // case and it's going to be reported by TCP correctly anyway given that similar thing happening 110 | // with TCP at the same time too is even rarer. 111 | fn handle_rendezvous( 112 | &mut self, 113 | ifc: &mut Interface, 114 | poll: &Poll, 115 | child: Token, 116 | deduced_nat_type: NatType, 117 | res: ::Res<(UdpSock, SocketAddr)>, 118 | ) { 119 | let r = match self.state { 120 | State::Rendezvous { 121 | ref mut children, 122 | ref mut info, 123 | ref mut nat_type, 124 | ref mut f, 125 | } => { 126 | let _ = children.remove(&child); 127 | if *nat_type == Default::default() { 128 | *nat_type = deduced_nat_type; 129 | } 130 | if let Ok((sock, ext_addr)) = res { 131 | info.0.push((sock, child)); 132 | info.1.push(ext_addr); 133 | } 134 | if children.is_empty() { 135 | let mut socks = mem::replace(&mut info.0, vec![]); 136 | let ext_addrs = mem::replace(&mut info.1, vec![]); 137 | let nat_type = mem::replace(nat_type, Default::default()); 138 | if socks.is_empty() || ext_addrs.is_empty() { 139 | UdpHolePunchMediator::dereg_socks(poll, &mut socks); 140 | f(ifc, poll, nat_type, Err(NatError::UdpRendezvousFailed)); 141 | Err(NatError::UdpRendezvousFailed) 142 | } else { 143 | f(ifc, poll, nat_type, Ok(ext_addrs)); 144 | Ok(Some(socks)) 145 | } 146 | } else { 147 | Ok(None) 148 | } 149 | } 150 | ref x => { 151 | warn!( 152 | "Logic Error in state book-keeping - Pls report this as a bug. Expected \ 153 | state: State::Rendezvous ;; Found: {:?}", 154 | x 155 | ); 156 | Err(NatError::InvalidState) 157 | } 158 | }; 159 | 160 | match r { 161 | Ok(Some(socks)) => self.state = State::ReadyToHolePunch(socks), 162 | Ok(None) => (), 163 | Err(e @ NatError::UdpRendezvousFailed) => { 164 | // This is reached only if children is empty. So no chance of borrow violation for 165 | // children in terminate() 166 | debug!("Terminating due to: {:?}", e); 167 | self.terminate(ifc, poll); 168 | } 169 | // Don't call terminate as that can lead to child being borrowed twice 170 | Err(e) => debug!("Ignoring error in handle rendezvous: {:?}", e), 171 | } 172 | } 173 | 174 | // Do not use callback to return success/errors in these functions. They are directly called so 175 | // give the result back asap, else there will be multiple borrows of the caller 176 | pub fn rendezvous_timeout( 177 | &mut self, 178 | ifc: &mut Interface, 179 | poll: &Poll, 180 | ) -> ::Res<(Vec, NatType)> { 181 | debug!("Timeout for UDP Rendezvous"); 182 | let r = match self.state { 183 | State::Rendezvous { 184 | ref mut children, 185 | ref mut info, 186 | ref mut nat_type, 187 | .. 188 | } => { 189 | UdpHolePunchMediator::terminate_children(ifc, poll, children); 190 | let mut socks = mem::replace(&mut info.0, vec![]); 191 | let ext_addrs = mem::replace(&mut info.1, vec![]); 192 | let nat_type = mem::replace(nat_type, Default::default()); 193 | if socks.is_empty() || ext_addrs.is_empty() { 194 | UdpHolePunchMediator::dereg_socks(poll, &mut socks); 195 | Err(NatError::UdpRendezvousFailed) 196 | } else { 197 | Ok((socks, ext_addrs, nat_type)) 198 | } 199 | } 200 | ref x => { 201 | trace!( 202 | "Already proceeded to the next state. Invalid state for executing a \ 203 | rendezvous timeout: {:?}", 204 | x 205 | ); 206 | Err(NatError::InvalidState) 207 | } 208 | }; 209 | 210 | let r = r.map(|(socks, ext_addrs, nat_type)| { 211 | self.state = State::ReadyToHolePunch(socks); 212 | (ext_addrs, nat_type) 213 | }); 214 | 215 | match r { 216 | Ok(_) | Err(NatError::InvalidState) => (), 217 | Err(ref e) => { 218 | debug!("Terminating due to: {:?}", e); 219 | self.terminate(ifc, poll); 220 | } 221 | } 222 | 223 | r 224 | } 225 | 226 | pub fn punch_hole( 227 | &mut self, 228 | ifc: &mut Interface, 229 | poll: &Poll, 230 | peers: Vec, 231 | peer_enc_pk: &box_::PublicKey, 232 | f: HolePunchFinsih, 233 | ) -> ::Res<()> { 234 | let info = match self.state { 235 | State::ReadyToHolePunch(ref mut info) => mem::replace(info, vec![]), 236 | ref x => { 237 | debug!("Improper state for this operation: {:?}", x); 238 | return Err(NatError::InvalidState); 239 | } 240 | }; 241 | 242 | let cap = info.len(); 243 | let hole_punchers_cfg = ifc.config().udp_hole_punchers.clone(); 244 | 245 | let mut children = HashSet::with_capacity(cap); 246 | for (((sock, token), peer), puncher_config) in info 247 | .into_iter() 248 | .zip(peers.into_iter()) 249 | .zip(hole_punchers_cfg.into_iter()) 250 | { 251 | let weak = self.self_weak.clone(); 252 | let handler = move |ifc: &mut Interface, poll: &Poll, token, res| { 253 | if let Some(mediator) = weak.upgrade() { 254 | mediator 255 | .borrow_mut() 256 | .handle_hole_punch(ifc, poll, token, res); 257 | } 258 | }; 259 | match Puncher::start( 260 | ifc, 261 | poll, 262 | token, 263 | sock, 264 | puncher_config.starting_ttl, 265 | puncher_config.ttl_increment_delay_ms, 266 | peer, 267 | peer_enc_pk, 268 | Box::new(handler), 269 | ) { 270 | Ok(()) => { 271 | let _ = children.insert(token); 272 | } 273 | Err(e) => debug!("Error: Could not start UDP Puncher: {:?}", e), 274 | } 275 | } 276 | 277 | if children.is_empty() { 278 | info!("Failure: Not even one valid child even managed to start hole punching"); 279 | self.terminate(ifc, poll); 280 | return Err(NatError::UdpHolePunchFailed); 281 | } 282 | 283 | self.state = State::HolePunching { children, f }; 284 | 285 | Ok(()) 286 | } 287 | 288 | fn handle_hole_punch( 289 | &mut self, 290 | ifc: &mut Interface, 291 | poll: &Poll, 292 | child: Token, 293 | res: ::Res<(UdpSock, SocketAddr, u32, u32, Duration)>, 294 | ) { 295 | let r = match self.state { 296 | State::HolePunching { 297 | ref mut children, 298 | ref mut f, 299 | } => { 300 | let _ = children.remove(&child); 301 | if let Ok((sock, peer, starting_ttl, ttl_on_being_reached, dur)) = res { 302 | f( 303 | ifc, 304 | poll, 305 | Ok(UdpHolePunchInfo::new( 306 | sock, 307 | peer, 308 | child, 309 | starting_ttl, 310 | ttl_on_being_reached, 311 | dur, 312 | )), 313 | ); 314 | Ok(true) 315 | } else if children.is_empty() { 316 | f(ifc, poll, Err(NatError::UdpHolePunchFailed)); 317 | Err(NatError::UdpHolePunchFailed) 318 | } else { 319 | Ok(false) 320 | } 321 | } 322 | ref x => { 323 | warn!( 324 | "Logic Error in state book-keeping - Pls report this as a bug. Expected \ 325 | state: State::HolePunching ;; Found: {:?}", 326 | x 327 | ); 328 | Err(NatError::InvalidState) 329 | } 330 | }; 331 | 332 | match r { 333 | Ok(true) => self.terminate(ifc, poll), 334 | Ok(false) => (), 335 | Err(e @ NatError::UdpHolePunchFailed) => { 336 | // This is reached only if children is empty, or we have removed the child that 337 | // called us. So no chance of borrow violation for children in terminate() 338 | debug!("Terminating due to: {:?}", e); 339 | self.terminate(ifc, poll); 340 | } 341 | // Don't call terminate as that can lead to child being borrowed twice 342 | Err(e) => debug!("Ignoring error in handle hole-punch: {:?}", e), 343 | } 344 | } 345 | 346 | fn terminate_children(ifc: &mut Interface, poll: &Poll, children: &mut HashSet) { 347 | for child in children.drain() { 348 | let child = match ifc.state(child) { 349 | Some(state) => state, 350 | None => continue, 351 | }; 352 | 353 | child.borrow_mut().terminate(ifc, poll); 354 | } 355 | } 356 | 357 | fn dereg_socks(poll: &Poll, socks: &mut Vec<(UdpSock, Token)>) { 358 | for (sock, _) in socks.drain(..) { 359 | let _ = poll.deregister(&sock); 360 | } 361 | } 362 | } 363 | 364 | impl NatState for UdpHolePunchMediator { 365 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 366 | match self.state { 367 | State::Rendezvous { 368 | ref mut children, 369 | ref mut info, 370 | .. 371 | } => { 372 | UdpHolePunchMediator::terminate_children(ifc, poll, children); 373 | UdpHolePunchMediator::dereg_socks(poll, &mut info.0); 374 | } 375 | State::ReadyToHolePunch(ref mut socks) => { 376 | UdpHolePunchMediator::dereg_socks(poll, socks) 377 | } 378 | State::HolePunching { 379 | ref mut children, .. 380 | } => { 381 | UdpHolePunchMediator::terminate_children(ifc, poll, children); 382 | } 383 | State::None => (), 384 | } 385 | } 386 | 387 | fn as_any(&mut self) -> &mut Any { 388 | self 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /src/udp/hole_punch/puncher.rs: -------------------------------------------------------------------------------- 1 | use mio::{Poll, PollOpt, Ready, Token}; 2 | use mio_extras::timer::Timeout; 3 | use socket_collection::UdpSock; 4 | use sodium::crypto::box_; 5 | use std::any::Any; 6 | use std::cell::RefCell; 7 | use std::net::SocketAddr; 8 | use std::rc::Rc; 9 | use std::time::{Duration, Instant}; 10 | use std::{fmt, mem}; 11 | use {Interface, NatError, NatState, NatTimer}; 12 | 13 | // Result of (UdpSock, peer, starting_ttl, ttl_on_being_reached, duration-of-hole-punch) 14 | pub type Finish = 15 | Box)>; 16 | 17 | const TIMER_ID: u8 = 0; 18 | const SYN: &[u8] = b"SYN"; 19 | const SYN_ACK: &[u8] = b"SYN-ACK"; 20 | const ACK: &[u8] = b"ACK"; 21 | const ACK_ACK: &[u8] = b"ACK_ACK"; // Fire and forget optimistic case to release ACK-sender 22 | const MAX_ACK_RETRANSMISSIONS: u8 = 3; 23 | 24 | #[derive(Debug, Eq, PartialEq)] 25 | enum Sending { 26 | Syn, 27 | SynAck, 28 | Ack, 29 | AckAck, 30 | } 31 | 32 | pub struct Puncher { 33 | token: Token, 34 | sock: UdpSock, 35 | peer: SocketAddr, 36 | peer_enc_pk: box_::PublicKey, 37 | key: box_::PrecomputedKey, 38 | connection_chooser: bool, 39 | os_ttl: u32, 40 | starting_ttl: u32, 41 | current_ttl: u32, 42 | ttl_on_being_reached: u32, 43 | ttl_inc_interval_ms: u64, 44 | timeout: Timeout, 45 | sending: Sending, 46 | num_acks_transmitted: u8, 47 | commenced_at: Instant, 48 | f: Finish, 49 | } 50 | 51 | impl Puncher { 52 | pub fn start( 53 | ifc: &mut Interface, 54 | poll: &Poll, 55 | token: Token, 56 | mut sock: UdpSock, 57 | starting_ttl: u8, 58 | ttl_inc_interval_ms: u64, 59 | peer: SocketAddr, 60 | peer_enc_pk: &box_::PublicKey, 61 | f: Finish, 62 | ) -> ::Res<()> { 63 | let os_ttl = sock.ttl()?; 64 | let starting_ttl = u32::from(starting_ttl); 65 | sock.set_ttl(starting_ttl)?; 66 | sock.connect(&peer).map_err(|e| { 67 | debug!("Error: Failed to connect UDP Puncher: {:?}", e); 68 | e 69 | })?; 70 | 71 | let timeout = ifc.set_timeout( 72 | Duration::from_millis(ttl_inc_interval_ms), 73 | NatTimer::new(token, TIMER_ID), 74 | ); 75 | 76 | if let Err(e) = poll.reregister( 77 | &sock, 78 | token, 79 | Ready::readable() | Ready::writable(), 80 | PollOpt::edge(), 81 | ) { 82 | debug!("Error: UdpPuncher errored in registeration: {:?}", e); 83 | let _ = poll.deregister(&sock); 84 | return Err(From::from(e)); 85 | } 86 | 87 | let puncher = Rc::new(RefCell::new(Puncher { 88 | token, 89 | sock, 90 | peer, 91 | peer_enc_pk: *peer_enc_pk, 92 | key: box_::precompute(peer_enc_pk, ifc.enc_sk()), 93 | connection_chooser: ifc.enc_pk() > peer_enc_pk, 94 | os_ttl, 95 | starting_ttl, 96 | current_ttl: starting_ttl, 97 | ttl_on_being_reached: 0, 98 | ttl_inc_interval_ms, 99 | timeout, 100 | sending: Sending::Syn, 101 | num_acks_transmitted: 0, 102 | commenced_at: Instant::now(), 103 | f, 104 | })); 105 | 106 | if let Err((nat_state, e)) = ifc.insert_state(token, puncher) { 107 | debug!("Error inserting state: {:?}", e); 108 | nat_state.borrow_mut().terminate(ifc, poll); 109 | return Err(NatError::UdpHolePunchFailed); 110 | } 111 | 112 | Ok(()) 113 | } 114 | 115 | fn read(&mut self, ifc: &mut Interface, poll: &Poll) { 116 | let mut cipher_text = Vec::new(); 117 | loop { 118 | match self.sock.read() { 119 | Ok(Some(m)) => cipher_text = m, 120 | Ok(None) => if cipher_text.is_empty() { 121 | return; 122 | } else { 123 | break; 124 | }, 125 | Err(e) => { 126 | trace!( 127 | "{} Ignoring Error Read: {:?} - These are likely caused by ICMP errors for \ 128 | timeout/ttl-drops or unreachable-peer/port. Continue with ttl-runners.", 129 | self, e); 130 | if cipher_text.is_empty() { 131 | return; 132 | } else { 133 | break; 134 | } 135 | } 136 | } 137 | } 138 | 139 | let msg = match ::msg_to_read(&cipher_text, &self.key) { 140 | Ok(m) => m, 141 | Err(e) => { 142 | debug!("{} Errored while deciphering incoming data: {:?}", self, e); 143 | return self.handle_err(ifc, poll); 144 | } 145 | }; 146 | 147 | if msg == SYN { 148 | if let Sending::Syn = self.sending { 149 | self.sending = Sending::SynAck; 150 | } 151 | } else if msg == SYN_ACK { 152 | if self.connection_chooser { 153 | self.sending = Sending::Ack 154 | } else if let Sending::Syn = self.sending { 155 | self.sending = Sending::SynAck; 156 | } 157 | } else if msg == ACK { 158 | if self.connection_chooser { 159 | info!( 160 | "{} No tolerance for a non chooser giving us an ACK - terminating", 161 | self 162 | ); 163 | return self.handle_err(ifc, poll); 164 | } 165 | self.sending = Sending::AckAck; 166 | } else if msg == ACK_ACK { 167 | if !self.connection_chooser { 168 | info!( 169 | "{} No tolerance for a chooser giving us an ACK_ACK - terminating", 170 | self 171 | ); 172 | return self.handle_err(ifc, poll); 173 | } 174 | trace!("{} Rxd ACK_ACK - we are done", self); 175 | return self.done(ifc, poll); 176 | } 177 | 178 | // Since we have read something, revert back to OS default TTL 179 | if self.current_ttl != self.os_ttl { 180 | if let Err(e) = self.sock.set_ttl(self.os_ttl) { 181 | debug!("{} Error: Could not set OS Default TTL: {:?}", self, e); 182 | return self.handle_err(ifc, poll); 183 | } else { 184 | self.ttl_on_being_reached = self.current_ttl; 185 | self.current_ttl = self.os_ttl; 186 | } 187 | } 188 | 189 | // Do a premature-handshake since we have received something. This will hasten things up. 190 | self.continue_handshake(ifc, poll); 191 | } 192 | 193 | fn write(&mut self, ifc: &mut Interface, poll: &Poll, m: Option>) { 194 | match self.sock.write(m.map(|m| (m, 0))) { 195 | Ok(true) => self.on_successful_send(ifc, poll), 196 | Ok(false) => (), 197 | Err(e) => trace!( 198 | "{} Ignoring Error in Write: {:?} - These are likely caused by ICMP errors for \ 199 | timeout/ttl-drops or unreachable-peer/port. Continue with ttl-runners.", 200 | self, 201 | e 202 | ), 203 | } 204 | } 205 | 206 | fn continue_handshake(&mut self, ifc: &mut Interface, poll: &Poll) { 207 | let msg = { 208 | let m = match self.sending { 209 | Sending::Syn => SYN, 210 | Sending::SynAck => SYN_ACK, 211 | Sending::Ack => { 212 | if self.num_acks_transmitted < MAX_ACK_RETRANSMISSIONS { 213 | self.num_acks_transmitted += 1; 214 | } 215 | if self.num_acks_transmitted == MAX_ACK_RETRANSMISSIONS { 216 | let _ = ifc.cancel_timeout(&self.timeout); 217 | } 218 | ACK 219 | } 220 | Sending::AckAck => { 221 | let _ = ifc.cancel_timeout(&self.timeout); 222 | ACK_ACK 223 | } 224 | }; 225 | 226 | match ::msg_to_send(m, &self.key) { 227 | Ok(m) => m, 228 | Err(e) => { 229 | debug!("{} Error: while encrypting: {:?}", self, e); 230 | return self.handle_err(ifc, poll); 231 | } 232 | } 233 | }; 234 | 235 | self.write(ifc, poll, Some(msg)); 236 | } 237 | 238 | fn on_successful_send(&mut self, ifc: &mut Interface, poll: &Poll) { 239 | match self.sending { 240 | Sending::Ack => if self.num_acks_transmitted == MAX_ACK_RETRANSMISSIONS { 241 | trace!("{} Sent all ACKs - we are done", self); 242 | self.done(ifc, poll); 243 | }, 244 | Sending::AckAck => { 245 | trace!("{} Sent ACK_ACK - we are done", self); 246 | self.done(ifc, poll); 247 | } 248 | _ => (), 249 | } 250 | } 251 | 252 | fn done(&mut self, ifc: &mut Interface, poll: &Poll) { 253 | let _ = ifc.remove_state(self.token); 254 | let _ = ifc.cancel_timeout(&self.timeout); 255 | let s = mem::replace(&mut self.sock, Default::default()); 256 | (*self.f)( 257 | ifc, 258 | poll, 259 | self.token, 260 | Ok(( 261 | s, 262 | self.peer, 263 | self.starting_ttl, 264 | self.ttl_on_being_reached, 265 | self.commenced_at.elapsed(), 266 | )), 267 | ); 268 | } 269 | 270 | fn handle_err(&mut self, ifc: &mut Interface, poll: &Poll) { 271 | self.terminate(ifc, poll); 272 | (*self.f)(ifc, poll, self.token, Err(NatError::UdpHolePunchFailed)); 273 | } 274 | } 275 | 276 | impl NatState for Puncher { 277 | fn ready(&mut self, ifc: &mut Interface, poll: &Poll, event: Ready) { 278 | if event.is_readable() { 279 | self.read(ifc, poll) 280 | } else if event.is_writable() { 281 | self.write(ifc, poll, None) 282 | } else { 283 | warn!( 284 | "{} Investigate: Ignoring unknown event kind: {:?}", 285 | self, event 286 | ); 287 | } 288 | } 289 | 290 | fn timeout(&mut self, ifc: &mut Interface, poll: &Poll, timer_id: u8) { 291 | if timer_id != TIMER_ID { 292 | warn!("{} Invalid Timer ID: {}", self, timer_id); 293 | } 294 | 295 | self.timeout = ifc.set_timeout( 296 | Duration::from_millis(self.ttl_inc_interval_ms), 297 | NatTimer::new(self.token, TIMER_ID), 298 | ); 299 | 300 | // If we have got an incoming message we would have had set current to the os value so 301 | // keep it at that, else do the usual incrementing. 302 | if self.current_ttl < self.os_ttl { 303 | self.current_ttl += 1; 304 | if self.current_ttl == self.os_ttl { 305 | debug!( 306 | "{} OS TTL reached and still peer did not reach us - giving up", 307 | self 308 | ); 309 | return self.handle_err(ifc, poll); 310 | } 311 | if let Err(e) = self.sock.set_ttl(self.current_ttl) { 312 | debug!("{} Error setting ttl: {:?}", self, e); 313 | return self.handle_err(ifc, poll); 314 | } 315 | } 316 | 317 | self.continue_handshake(ifc, poll) 318 | } 319 | 320 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 321 | let _ = ifc.remove_state(self.token); 322 | let _ = ifc.cancel_timeout(&self.timeout); 323 | let _ = poll.deregister(&self.sock); 324 | 325 | trace!("{} terminated", self); 326 | } 327 | 328 | fn as_any(&mut self) -> &mut Any { 329 | self 330 | } 331 | } 332 | 333 | impl fmt::Display for Puncher { 334 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 335 | let ttl_on_being_reached = if self.ttl_on_being_reached == 0 { 336 | "N/A".to_string() 337 | } else { 338 | format!("{}", self.ttl_on_being_reached) 339 | }; 340 | write!( 341 | f, 342 | "UdpPuncher [peer=({:02x}{:02x}{:02x}../{}) ;; os_ttl={}, starting_ttl={}, current_ttl={} 343 | , ttl_on_being_reached={} ;; state={:?} ;; chooser={}]", 344 | self.peer_enc_pk.0[0], 345 | self.peer_enc_pk.0[1], 346 | self.peer_enc_pk.0[2], 347 | self.peer, 348 | self.os_ttl, 349 | self.starting_ttl, 350 | self.current_ttl, 351 | ttl_on_being_reached, 352 | self.sending, 353 | self.connection_chooser 354 | ) 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /src/udp/hole_punch/rendezvous_client.rs: -------------------------------------------------------------------------------- 1 | use mio::{Poll, PollOpt, Ready, Token}; 2 | use rand::{self, Rng}; 3 | use socket_collection::UdpSock; 4 | use sodium::crypto::sealedbox; 5 | use std::any::Any; 6 | use std::cell::RefCell; 7 | use std::mem; 8 | use std::net::SocketAddr; 9 | use std::rc::Rc; 10 | use std::str::{self, FromStr}; 11 | use udp::{UdpEchoReq, UdpEchoResp}; 12 | use {Interface, NatError, NatState, NatType}; 13 | 14 | pub type Finish = Box)>; 15 | 16 | /// UDP rendezvous client that queries the server for it's public endpoint - IP:port. 17 | pub struct UdpRendezvousClient { 18 | sock: UdpSock, 19 | token: Token, 20 | servers: Vec, 21 | on_first_write_triggered: Option, 22 | // Note: DO NOT convert to Hash/BTreeSet - strict ordering is required to pair with peer 23 | // More info in `RendezvousInfo` documentation 24 | our_ext_addrs: Vec, 25 | f: Finish, 26 | } 27 | 28 | impl UdpRendezvousClient { 29 | /// Starts sending queries to multiple servers. Servers are retrieved from `ifc.config()`. 30 | pub fn start(ifc: &mut Interface, poll: &Poll, sock: UdpSock, f: Finish) -> ::Res { 31 | let token = ifc.new_token(); 32 | let mut servers = ifc.config().remote_udp_rendezvous_servers.clone(); 33 | let num_servers = servers.len(); 34 | if num_servers < 2 { 35 | info!( 36 | "Udp: Symmetric NAT detection and port prediction will not be possible using \ 37 | less than 2 Rendezvous Servers. Use at-least 2. Recommended is 3." 38 | ); 39 | } else if num_servers > 3 { 40 | let mut rng = rand::thread_rng(); 41 | rng.shuffle(&mut servers); 42 | servers = servers[..3].to_owned(); 43 | } 44 | 45 | let server = match servers.pop() { 46 | Some(server) => server, 47 | None => return Err(NatError::UdpRendezvousFailed), 48 | }; 49 | 50 | poll.register( 51 | &sock, 52 | token, 53 | Ready::readable() | Ready::writable(), 54 | PollOpt::edge(), 55 | )?; 56 | 57 | let client = Rc::new(RefCell::new(UdpRendezvousClient { 58 | sock, 59 | token, 60 | servers, 61 | on_first_write_triggered: Some(server), 62 | our_ext_addrs: Vec::with_capacity(num_servers), 63 | f, 64 | })); 65 | 66 | if ifc.insert_state(token, client.clone()).is_err() { 67 | debug!("Unable to insert UdpRendezvousClient State!"); 68 | client.borrow_mut().terminate(ifc, poll); 69 | Err(NatError::UdpRendezvousFailed) 70 | } else { 71 | Ok(token) 72 | } 73 | } 74 | 75 | fn read(&mut self, ifc: &mut Interface, poll: &Poll) { 76 | let mut cipher_text = Vec::with_capacity(64); 77 | loop { 78 | match self.sock.read() { 79 | Ok(Some(UdpEchoResp(cipher))) => cipher_text = cipher, 80 | Ok(None) => if cipher_text.is_empty() { 81 | return; 82 | } else { 83 | break; 84 | }, 85 | Err(e) => { 86 | debug!( 87 | "Error: Udp Rendezvous Client has errored out in read: {:?}", 88 | e 89 | ); 90 | return self.terminate(ifc, poll); 91 | } 92 | } 93 | } 94 | 95 | if let Ok(our_ext_addr_bytes) = sealedbox::open(&cipher_text, ifc.enc_pk(), ifc.enc_sk()) { 96 | match str::from_utf8(&our_ext_addr_bytes) { 97 | Ok(our_ext_addr_str) => match SocketAddr::from_str(our_ext_addr_str) { 98 | Ok(addr) => self.our_ext_addrs.push(addr), 99 | Err(e) => { 100 | debug!( 101 | "Ignoring UdpEchoResp which contained non-parsable address: \ 102 | {:?}", 103 | e 104 | ); 105 | } 106 | }, 107 | Err(e) => debug!( 108 | "Ignoring UdpEchoResp which contained non-utf8 address: {:?}", 109 | e 110 | ), 111 | } 112 | } else { 113 | debug!("Ignoring UdpEchoResp which could not be decrypted."); 114 | } 115 | 116 | if let Some(server) = self.servers.pop() { 117 | if let Err(e) = self.sock.connect(&server) { 118 | debug!( 119 | "Error: Udp Rendezvous Client could not connect to server: {:?}", 120 | e 121 | ); 122 | return self.handle_err(ifc, poll, None); 123 | } 124 | let pk = ifc.enc_pk().0; 125 | self.write(ifc, poll, Some(UdpEchoReq(pk))); 126 | } else { 127 | self.done(ifc, poll) 128 | } 129 | } 130 | 131 | fn write(&mut self, ifc: &mut Interface, poll: &Poll, m: Option) { 132 | if let Err(e) = self.sock.write(m.map(|m| (m, 0))) { 133 | debug!("Udp Rendezvous Client has errored out in write: {:?}", e); 134 | self.handle_err(ifc, poll, None) 135 | } 136 | } 137 | 138 | fn done(&mut self, ifc: &mut Interface, poll: &Poll) { 139 | let _ = ifc.remove_state(self.token); 140 | 141 | let mut ext_addr = match self.our_ext_addrs.pop() { 142 | Some(addr) => addr, 143 | None => return self.handle_err(ifc, poll, None), 144 | }; 145 | 146 | let mut nat_type = Default::default(); 147 | 148 | let mut addrs = vec![ext_addr]; 149 | let mut port_prediction_offset = 0i32; 150 | let mut is_err = false; 151 | for addr in &self.our_ext_addrs { 152 | addrs.push(*addr); 153 | 154 | if ext_addr.ip() != addr.ip() { 155 | warn!( 156 | "Symmetric NAT with variable IP mapping detected. No logic for Udp \ 157 | external address prediction for these circumstances!" 158 | ); 159 | nat_type = NatType::EDMRandomIp(addrs.into_iter().map(|s| s.ip()).collect()); 160 | is_err = true; 161 | break; 162 | } else if addrs.len() == 2 { 163 | port_prediction_offset = i32::from(addr.port()) - i32::from(ext_addr.port()); 164 | } else if port_prediction_offset != i32::from(addr.port()) - i32::from(ext_addr.port()) 165 | { 166 | warn!( 167 | "Symmetric NAT with non-uniformly changing port mapping detected. No logic \ 168 | for Udp external address prediction for these circumstances!" 169 | ); 170 | nat_type = NatType::EDMRandomPort(addrs.into_iter().map(|s| s.port()).collect()); 171 | is_err = true; 172 | break; 173 | } 174 | 175 | ext_addr = *addr; 176 | } 177 | 178 | if is_err { 179 | return self.handle_err(ifc, poll, Some(nat_type)); 180 | } 181 | 182 | let port = ext_addr.port(); 183 | ext_addr.set_port((i32::from(port) + port_prediction_offset) as u16); 184 | trace!("Our ext addr by Udp Rendezvous Client: {}", ext_addr); 185 | 186 | nat_type = if port_prediction_offset == 0 { 187 | NatType::EIM 188 | } else { 189 | NatType::EDM(port_prediction_offset) 190 | }; 191 | 192 | let s = mem::replace(&mut self.sock, Default::default()); 193 | (*self.f)(ifc, poll, self.token, nat_type, Ok((s, ext_addr))); 194 | } 195 | 196 | fn handle_err(&mut self, ifc: &mut Interface, poll: &Poll, nat_type: Option) { 197 | self.terminate(ifc, poll); 198 | (*self.f)( 199 | ifc, 200 | poll, 201 | self.token, 202 | nat_type.unwrap_or_default(), 203 | Err(NatError::UdpRendezvousFailed), 204 | ); 205 | } 206 | } 207 | 208 | impl NatState for UdpRendezvousClient { 209 | fn ready(&mut self, ifc: &mut Interface, poll: &Poll, event: Ready) { 210 | if event.is_readable() { 211 | self.read(ifc, poll) 212 | } else if event.is_writable() { 213 | if let Some(server) = self.on_first_write_triggered.take() { 214 | if let Err(e) = self.sock.connect(&server) { 215 | debug!( 216 | "Error: Udp Rendezvous Client could not connect to server: {:?}", 217 | e 218 | ); 219 | return self.handle_err(ifc, poll, None); 220 | } 221 | let pk = ifc.enc_pk().0; 222 | self.write(ifc, poll, Some(UdpEchoReq(pk))); 223 | } else { 224 | self.write(ifc, poll, None); 225 | } 226 | } else { 227 | warn!("Investigate: Ignoring unknown event kind: {:?}", event); 228 | } 229 | } 230 | 231 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 232 | let _ = ifc.remove_state(self.token); 233 | let _ = poll.deregister(&self.sock); 234 | } 235 | 236 | fn as_any(&mut self) -> &mut Any { 237 | self 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/udp/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::hole_punch::UdpHolePunchMediator; 2 | pub use self::rendezvous_server::UdpRendezvousServer; 3 | 4 | use sodium::crypto::box_::PUBLICKEYBYTES; 5 | 6 | mod hole_punch; 7 | mod rendezvous_server; 8 | 9 | #[derive(Debug, Serialize, Deserialize)] 10 | struct UdpEchoReq(pub [u8; PUBLICKEYBYTES]); 11 | #[derive(Debug, Serialize, Deserialize)] 12 | struct UdpEchoResp(pub Vec); 13 | -------------------------------------------------------------------------------- /src/udp/rendezvous_server.rs: -------------------------------------------------------------------------------- 1 | use super::{UdpEchoReq, UdpEchoResp}; 2 | use config::UDP_RENDEZVOUS_PORT; 3 | use mio::{Poll, PollOpt, Ready, Token}; 4 | use socket_collection::UdpSock; 5 | use sodium::crypto::box_; 6 | use sodium::crypto::sealedbox; 7 | use std::any::Any; 8 | use std::cell::RefCell; 9 | use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; 10 | use std::rc::Rc; 11 | use {Interface, NatError, NatState}; 12 | 13 | /// UDP Rendezvous server. 14 | /// 15 | /// This is intended to be kept running on some publicly reachable endpoint so that peers can 16 | /// obtain their rendezvous information. The information provided back to the peer is encrypted 17 | /// with peer-supplied asymmetric key. Certain router and firewalls scan the packet and if they 18 | /// find an IP address belonging to their pool that they use to do the NAT mapping/translation, 19 | /// they take it as a STUN attempt or similar and mangle the information or even discard it. 20 | /// Encrypting makes sure no such thing happens. 21 | pub struct UdpRendezvousServer { 22 | sock: UdpSock, 23 | token: Token, 24 | terminated: bool, 25 | } 26 | 27 | impl UdpRendezvousServer { 28 | /// Boot the UDP Rendezvous server. This should normally be called only once. 29 | /// Uses the bind port specified by `Interface::config()`. If port is 0, OS chooses a random 30 | /// available port, which you can find out by checking the return value. 31 | /// 32 | /// # Returns 33 | /// 34 | /// A tuple of mio token associated with the rendezvous server and local listen address. 35 | pub fn start(ifc: &mut Interface, poll: &Poll) -> ::Res<(Token, SocketAddr)> { 36 | let port = ifc 37 | .config() 38 | .udp_rendezvous_port 39 | .unwrap_or(UDP_RENDEZVOUS_PORT); 40 | let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port)); 41 | let sock = UdpSock::bind(&addr)?; 42 | let server_addr = sock.local_addr()?; 43 | 44 | let token = ifc.new_token(); 45 | poll.register( 46 | &sock, 47 | token, 48 | Ready::readable() | Ready::writable(), 49 | PollOpt::edge(), 50 | )?; 51 | 52 | let server = Rc::new(RefCell::new(UdpRendezvousServer { 53 | sock, 54 | token, 55 | terminated: false, 56 | })); 57 | 58 | if ifc.insert_state(token, server.clone()).is_err() { 59 | warn!("Unable to start UdpRendezvousServer!"); 60 | server.borrow_mut().terminate(ifc, poll); 61 | Err(NatError::UdpRendezvousServerStartFailed) 62 | } else { 63 | Ok((token, server_addr)) 64 | } 65 | } 66 | 67 | fn read_frm(&mut self, ifc: &mut Interface, poll: &Poll) { 68 | let mut peers = Vec::new(); 69 | loop { 70 | match self.sock.read_frm() { 71 | Ok(Some((UdpEchoReq(pk), peer))) => peers.push((box_::PublicKey(pk), peer)), 72 | Ok(None) => if peers.is_empty() { 73 | return; 74 | } else { 75 | break; 76 | }, 77 | Err(e) => { 78 | debug!("Error in read: {:?}", e); 79 | return self.terminate(ifc, poll); 80 | } 81 | } 82 | } 83 | 84 | for (pk, peer) in peers { 85 | let resp = UdpEchoResp(sealedbox::seal(format!("{}", peer).as_bytes(), &pk)); 86 | self.write_to(ifc, poll, Some((resp, peer))); 87 | // Errored while writting 88 | if self.terminated { 89 | break; 90 | } 91 | } 92 | } 93 | 94 | fn write_to(&mut self, ifc: &mut Interface, poll: &Poll, m: Option<(UdpEchoResp, SocketAddr)>) { 95 | match self.sock.write_to(m.map(|(m, s)| (m, s, 0))) { 96 | Ok(_) => (), 97 | Err(e) => { 98 | warn!("Udp Rendezvous Server has errored out in write: {:?}", e); 99 | self.terminate(ifc, poll); 100 | } 101 | } 102 | } 103 | } 104 | 105 | impl NatState for UdpRendezvousServer { 106 | fn ready(&mut self, ifc: &mut Interface, poll: &Poll, event: Ready) { 107 | if event.is_readable() { 108 | self.read_frm(ifc, poll) 109 | } else if event.is_writable() { 110 | self.write_to(ifc, poll, None) 111 | } else { 112 | warn!("Investigate: Ignoring unknown event kind: {:?}", event); 113 | } 114 | } 115 | 116 | fn terminate(&mut self, ifc: &mut Interface, poll: &Poll) { 117 | let _ = ifc.remove_state(self.token); 118 | let _ = poll.deregister(&self.sock); 119 | self.terminated = true; 120 | } 121 | 122 | fn as_any(&mut self) -> &mut Any { 123 | self 124 | } 125 | } 126 | 127 | #[cfg(test)] 128 | mod tests { 129 | use super::*; 130 | use std::net::SocketAddr; 131 | use std::sync::mpsc; 132 | use test_utils::spawn_event_loop; 133 | use udp::hole_punch::UdpRendezvousClient; 134 | use {Config, NatMsg}; 135 | 136 | /// Creates config for tests with all values zeroed. 137 | fn p2p_test_cfg() -> Config { 138 | Config { 139 | rendezvous_timeout_sec: None, 140 | hole_punch_timeout_sec: None, 141 | hole_punch_wait_for_other: None, 142 | udp_rendezvous_port: None, 143 | tcp_rendezvous_port: None, 144 | remote_udp_rendezvous_servers: Vec::new(), 145 | remote_tcp_rendezvous_servers: Vec::new(), 146 | udp_hole_punchers: Vec::new(), 147 | } 148 | } 149 | 150 | #[test] 151 | fn it_responds_with_client_address() { 152 | let mut config = p2p_test_cfg(); 153 | config.udp_rendezvous_port = Some(0); 154 | let server_el = spawn_event_loop(config); 155 | let (server_port_tx, server_port_rx) = mpsc::channel(); 156 | unwrap!(server_el.nat_tx.send(NatMsg::new(move |ifc, poll| { 157 | let (_token, addr) = unwrap!(UdpRendezvousServer::start(ifc, poll)); 158 | unwrap!(server_port_tx.send(addr.port())); 159 | }))); 160 | 161 | let server_port = unwrap!(server_port_rx.recv()); 162 | let server_addr = 163 | SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), server_port)); 164 | let (addr_tx, addr_rx) = mpsc::channel(); 165 | let mut config = p2p_test_cfg(); 166 | config.remote_udp_rendezvous_servers = vec![server_addr]; 167 | let client_el = spawn_event_loop(config); 168 | let addr = unwrap!("127.0.0.1:0".parse()); 169 | let sock = unwrap!(UdpSock::bind(&addr)); 170 | let exp_client_addr = unwrap!(sock.local_addr()); 171 | 172 | unwrap!(client_el.nat_tx.send(NatMsg::new(move |ifc, poll| { 173 | let on_done = Box::new( 174 | move |_ifc: &mut Interface, 175 | _poll: &Poll, 176 | _child, 177 | _nat_type, 178 | res: ::Res<(UdpSock, SocketAddr)>| { 179 | let client_addr = unwrap!(res).1; 180 | unwrap!(addr_tx.send(client_addr)); 181 | }, 182 | ); 183 | let _ = unwrap!(UdpRendezvousClient::start(ifc, poll, sock, on_done)); 184 | }))); 185 | 186 | let client_addr = unwrap!(addr_rx.recv()); 187 | assert_eq!(client_addr, exp_client_addr); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /tests/nat-traversal-test-resources/config-peers: -------------------------------------------------------------------------------- 1 | { 2 | "rendezvous_timeout_sec": null, 3 | "hole_punch_timeout_sec": null, 4 | "hole_punch_wait_for_other": null, 5 | "udp_rendezvous_port": null, 6 | "tcp_rendezvous_port": null, 7 | "remote_udp_rendezvous_servers": [ 8 | "127.0.0.1:10010", 9 | "127.0.0.1:10011", 10 | "127.0.0.1:10012" 11 | ], 12 | "remote_tcp_rendezvous_servers": [ 13 | "127.0.0.1:15010", 14 | "127.0.0.1:15011", 15 | "127.0.0.1:15012" 16 | ], 17 | "udp_hole_punchers": [ 18 | { 19 | "starting_ttl": 2, 20 | "ttl_increment_delay_ms": 500 21 | }, 22 | { 23 | "starting_ttl": 5, 24 | "ttl_increment_delay_ms": 500 25 | }, 26 | { 27 | "starting_ttl": 10, 28 | "ttl_increment_delay_ms": 500 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /tests/nat-traversal-test-resources/config-rendezvous-server-0: -------------------------------------------------------------------------------- 1 | { 2 | "rendezvous_timeout_sec": null, 3 | "hole_punch_timeout_sec": null, 4 | "hole_punch_wait_for_other": null, 5 | "udp_rendezvous_port": 10010, 6 | "tcp_rendezvous_port": 15010, 7 | "remote_udp_rendezvous_servers": [], 8 | "remote_tcp_rendezvous_servers": [], 9 | "udp_hole_punchers": [] 10 | } 11 | -------------------------------------------------------------------------------- /tests/nat-traversal-test-resources/config-rendezvous-server-1: -------------------------------------------------------------------------------- 1 | { 2 | "rendezvous_timeout_sec": null, 3 | "hole_punch_timeout_sec": null, 4 | "hole_punch_wait_for_other": null, 5 | "udp_rendezvous_port": 10011, 6 | "tcp_rendezvous_port": 15011, 7 | "remote_udp_rendezvous_servers": [], 8 | "remote_tcp_rendezvous_servers": [], 9 | "udp_hole_punchers": [] 10 | } 11 | -------------------------------------------------------------------------------- /tests/nat-traversal-test-resources/config-rendezvous-server-2: -------------------------------------------------------------------------------- 1 | { 2 | "rendezvous_timeout_sec": null, 3 | "hole_punch_timeout_sec": null, 4 | "hole_punch_wait_for_other": null, 5 | "udp_rendezvous_port": 10012, 6 | "tcp_rendezvous_port": 15012, 7 | "remote_udp_rendezvous_servers": [], 8 | "remote_tcp_rendezvous_servers": [], 9 | "udp_hole_punchers": [] 10 | } 11 | -------------------------------------------------------------------------------- /tests/nat-traversal-test-resources/sample-log.toml: -------------------------------------------------------------------------------- 1 | [appenders.display] 2 | kind = "async_console" 3 | pattern = "{l} {d(%H:%M:%S.%f)} [{M} #FS#{f}#FE#:{L}] {m}{n}" 4 | 5 | [[appenders.display.filters]] 6 | kind = "threshold" 7 | level = "info" 8 | 9 | [appenders.async_file] 10 | kind = "async_file" 11 | output_file_name = "client.log" 12 | pattern = "{l} {d(%H:%M:%S.%f)} [{M} #FS#{f}#FE#:{L}] {m}{n}" 13 | append = true 14 | file_timestamp = true 15 | 16 | [loggers."p2p"] 17 | level = "trace" 18 | appenders = ["display", "async_file"] 19 | 20 | [loggers."nat_traversal"] 21 | level = "trace" 22 | appenders = ["display", "async_file"] 23 | 24 | [loggers."socket_collection"] 25 | level = "info" 26 | appenders = ["display", "async_file"] 27 | -------------------------------------------------------------------------------- /tests/nat_traversal.rs: -------------------------------------------------------------------------------- 1 | extern crate maidsafe_utilities; 2 | extern crate mio; 3 | extern crate mio_extras; 4 | extern crate p2p; 5 | extern crate rust_sodium as sodium; 6 | extern crate serde_json; 7 | #[macro_use] 8 | extern crate unwrap; 9 | 10 | use mio::Poll; 11 | use p2p::{ 12 | Config, Handle, HolePunchMediator, Interface, NatInfo, NatMsg, NatState, NatTimer, NatType, 13 | QueuedNotifier, RendezvousInfo, Res, TcpRendezvousServer, UdpRendezvousServer, 14 | }; 15 | use std::sync::mpsc; 16 | use utils::{read_config, spawn_event_loop, EventLoop}; 17 | 18 | #[path = "../src/test_utils.rs"] 19 | mod utils; 20 | 21 | fn start_rendezvous_servers() -> Vec { 22 | const NUM_RENDEZVOUS_SERVERS: usize = 3; 23 | 24 | let mut els = Vec::new(); 25 | 26 | for i in 0..NUM_RENDEZVOUS_SERVERS { 27 | let el = spawn_event_loop(read_config(&format!( 28 | "./tests/nat-traversal-test-resources/config-rendezvous-server-{}", 29 | i, 30 | ))); 31 | 32 | let (tx, rx) = mpsc::channel(); 33 | unwrap!(el.nat_tx.send(NatMsg::new(move |ifc, poll| { 34 | let udp_server_token = unwrap!(UdpRendezvousServer::start(ifc, poll)); 35 | let tcp_server_token = unwrap!(TcpRendezvousServer::start(ifc, poll)); 36 | unwrap!(tx.send((udp_server_token, tcp_server_token))); 37 | }))); 38 | 39 | let (_udp_server_token, _tcp_server_token) = unwrap!(rx.recv()); 40 | 41 | els.push(el); 42 | } 43 | 44 | els 45 | } 46 | 47 | fn get_rendezvous_info(el: &EventLoop) -> mpsc::Receiver<(NatInfo, Res<(Handle, RendezvousInfo)>)> { 48 | let (tx, rx) = mpsc::channel(); 49 | unwrap!(el.nat_tx.send(NatMsg::new(move |ifc, poll| { 50 | let handler = move |_: &mut Interface, _: &Poll, (nat_info, res)| { 51 | unwrap!(tx.send((nat_info, res))); 52 | }; 53 | let _mediator_token = unwrap!(HolePunchMediator::start( 54 | ifc, 55 | poll, 56 | QueuedNotifier::new(handler) 57 | )); 58 | }))); 59 | 60 | rx 61 | } 62 | 63 | #[test] 64 | fn nat_traverse_among_3_peers() { 65 | unwrap!(maidsafe_utilities::log::init(true)); 66 | 67 | let _els_rendezvous_servers = start_rendezvous_servers(); 68 | 69 | let peer_config_path = "./tests/nat-traversal-test-resources/config-peers".to_string(); 70 | let el_peer0 = spawn_event_loop(read_config(&peer_config_path)); 71 | let el_peer1 = spawn_event_loop(read_config(&peer_config_path)); 72 | let el_peer2 = spawn_event_loop(read_config(&peer_config_path)); 73 | 74 | // Get `RendezvousInfo` in parallel 75 | let rendezvous_rx01 = get_rendezvous_info(&el_peer0); 76 | let rendezvous_rx02 = get_rendezvous_info(&el_peer0); 77 | let rendezvous_rx10 = get_rendezvous_info(&el_peer1); 78 | let rendezvous_rx12 = get_rendezvous_info(&el_peer1); 79 | let rendezvous_rx20 = get_rendezvous_info(&el_peer2); 80 | let rendezvous_rx21 = get_rendezvous_info(&el_peer2); 81 | 82 | let (nat_info01, (handle01, rendezvous_info01)) = { 83 | let (nat_info, res) = unwrap!(rendezvous_rx01.recv()); 84 | (nat_info, unwrap!(res)) 85 | }; 86 | let (nat_info02, (handle02, rendezvous_info02)) = { 87 | let (nat_info, res) = unwrap!(rendezvous_rx02.recv()); 88 | (nat_info, unwrap!(res)) 89 | }; 90 | let (nat_info10, (handle10, rendezvous_info10)) = { 91 | let (nat_info, res) = unwrap!(rendezvous_rx10.recv()); 92 | (nat_info, unwrap!(res)) 93 | }; 94 | let (nat_info12, (handle12, rendezvous_info12)) = { 95 | let (nat_info, res) = unwrap!(rendezvous_rx12.recv()); 96 | (nat_info, unwrap!(res)) 97 | }; 98 | let (nat_info20, (handle20, rendezvous_info20)) = { 99 | let (nat_info, res) = unwrap!(rendezvous_rx20.recv()); 100 | (nat_info, unwrap!(res)) 101 | }; 102 | let (nat_info21, (handle21, rendezvous_info21)) = { 103 | let (nat_info, res) = unwrap!(rendezvous_rx21.recv()); 104 | (nat_info, unwrap!(res)) 105 | }; 106 | 107 | // The localhost is very likely to be EIM unless someone's changed it deliberately for e.g., in 108 | // iptables on Linux etc. In that case change the assertion accordingly. 109 | assert_eq!(nat_info01.nat_type_for_tcp, NatType::EIM); 110 | assert_eq!(nat_info02.nat_type_for_tcp, NatType::EIM); 111 | assert_eq!(nat_info10.nat_type_for_tcp, NatType::EIM); 112 | assert_eq!(nat_info12.nat_type_for_tcp, NatType::EIM); 113 | assert_eq!(nat_info20.nat_type_for_tcp, NatType::EIM); 114 | assert_eq!(nat_info21.nat_type_for_tcp, NatType::EIM); 115 | 116 | // The localhost is very likely to be EIM unless someone's changed it deliberately for e.g., in 117 | // iptables on Linux etc. In that case change the assertion accordingly. 118 | assert_eq!(nat_info01.nat_type_for_udp, NatType::EIM); 119 | assert_eq!(nat_info02.nat_type_for_udp, NatType::EIM); 120 | assert_eq!(nat_info10.nat_type_for_udp, NatType::EIM); 121 | assert_eq!(nat_info12.nat_type_for_udp, NatType::EIM); 122 | assert_eq!(nat_info20.nat_type_for_udp, NatType::EIM); 123 | assert_eq!(nat_info21.nat_type_for_udp, NatType::EIM); 124 | 125 | // NAT Traverse in parallel 126 | let (hole_punch_tx01, hole_punch_rx01) = mpsc::channel(); 127 | handle01.fire_hole_punch(rendezvous_info10, move |_, _, res| { 128 | unwrap!(hole_punch_tx01.send(res)); 129 | }); 130 | let (hole_punch_tx02, hole_punch_rx02) = mpsc::channel(); 131 | handle02.fire_hole_punch(rendezvous_info20, move |_, _, res| { 132 | unwrap!(hole_punch_tx02.send(res)); 133 | }); 134 | let (hole_punch_tx10, hole_punch_rx10) = mpsc::channel(); 135 | handle10.fire_hole_punch(rendezvous_info01, move |_, _, res| { 136 | unwrap!(hole_punch_tx10.send(res)); 137 | }); 138 | let (hole_punch_tx12, hole_punch_rx12) = mpsc::channel(); 139 | handle12.fire_hole_punch(rendezvous_info21, move |_, _, res| { 140 | unwrap!(hole_punch_tx12.send(res)); 141 | }); 142 | let (hole_punch_tx20, hole_punch_rx20) = mpsc::channel(); 143 | handle20.fire_hole_punch(rendezvous_info02, move |_, _, res| { 144 | unwrap!(hole_punch_tx20.send(res)); 145 | }); 146 | let (hole_punch_tx21, hole_punch_rx21) = mpsc::channel(); 147 | handle21.fire_hole_punch(rendezvous_info12, move |_, _, res| { 148 | unwrap!(hole_punch_tx21.send(res)); 149 | }); 150 | 151 | let hole_punch_info01 = unwrap!(unwrap!(hole_punch_rx01.recv())); 152 | let hole_punch_info02 = unwrap!(unwrap!(hole_punch_rx02.recv())); 153 | let hole_punch_info10 = unwrap!(unwrap!(hole_punch_rx10.recv())); 154 | let hole_punch_info12 = unwrap!(unwrap!(hole_punch_rx12.recv())); 155 | let hole_punch_info20 = unwrap!(unwrap!(hole_punch_rx20.recv())); 156 | let hole_punch_info21 = unwrap!(unwrap!(hole_punch_rx21.recv())); 157 | 158 | assert!(hole_punch_info01.tcp.is_some()); 159 | assert!(hole_punch_info02.tcp.is_some()); 160 | assert!(hole_punch_info10.tcp.is_some()); 161 | assert!(hole_punch_info12.tcp.is_some()); 162 | assert!(hole_punch_info20.tcp.is_some()); 163 | assert!(hole_punch_info21.tcp.is_some()); 164 | 165 | assert!(hole_punch_info01.udp.is_some()); 166 | assert!(hole_punch_info02.udp.is_some()); 167 | assert!(hole_punch_info10.udp.is_some()); 168 | assert!(hole_punch_info12.udp.is_some()); 169 | assert!(hole_punch_info20.udp.is_some()); 170 | assert!(hole_punch_info21.udp.is_some()); 171 | } 172 | --------------------------------------------------------------------------------