From c591c6a43a0d0b2b858c61b71f51b46e17d4b15c Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 11 Feb 2016 18:01:56 +0800 Subject: [PATCH 1/4] Clean up. Enrich error handling for punch_hole function. --- src/hole_punch_server_addr.rs | 31 -------- src/lib.rs | 13 +--- src/mapped_tcp_socket.rs | 10 +-- src/mapped_udp_socket.rs | 26 +++---- src/mapping_context.rs | 5 -- src/periodic_sender.rs | 97 ------------------------- src/punched_udp_socket.rs | 108 +++++++++++++++++++++++----- src/simple_udp_hole_punch_server.rs | 11 ++- src/socket_utils.rs | 5 +- 9 files changed, 116 insertions(+), 190 deletions(-) delete mode 100644 src/hole_punch_server_addr.rs delete mode 100644 src/periodic_sender.rs diff --git a/src/hole_punch_server_addr.rs b/src/hole_punch_server_addr.rs deleted file mode 100644 index 10e5ae38..00000000 --- a/src/hole_punch_server_addr.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2016 MaidSafe.net limited. -// -// This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, -// version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which -// licence you accepted on initial access to the Software (the "Licences"). -// -// By contributing code to the SAFE Network Software, or to this project generally, you agree to be -// bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the -// Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. -// -// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed -// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. -// -// Please review the Licences for the specific language governing permissions and limitations -// relating to use of the SAFE Network Software. - -//! # `nat_traversal` -//! NAT traversal utilities. - -use std::net::SocketAddrV4; -use igd; - -/// The address of a server that can be used to obtain an external address. -pub enum HolePunchServerAddr { - /// A server which speaks the simple hole punching protocol (ie. our MaidSafe protocol). This - /// should probably be deprecated and replaced with a proper STUN implementation. - Simple(SocketAddrV4), - /// An Internet Gateway Device that can be used for UPnP port mapping. - IgdGateway(igd::Gateway) -} diff --git a/src/lib.rs b/src/lib.rs index 45ddc807..143d0810 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,36 +41,29 @@ #![cfg_attr(feature="clippy", plugin(clippy))] #![cfg_attr(feature="clippy", deny(clippy, clippy_pedantic))] -// TODO - remove this -#![allow(unused, unused_extern_crates)] - // TODO(canndrew): Remove this once this: https://github.com/tailhook/quick-error/issues/18 // is fixed. #![allow(missing_docs)] extern crate cbor; -#[macro_use] -extern crate log; extern crate net2; extern crate rand; extern crate rustc_serialize; extern crate time; -extern crate crossbeam; extern crate void; #[macro_use] extern crate maidsafe_utilities; extern crate igd; extern crate socket_addr; extern crate get_if_addrs; -extern crate sodiumoxide; -extern crate libc; +//extern crate libc; extern crate w_result; extern crate ip; +#[allow(unused_extern_crates)] // Needed because the crate is only used for macros #[macro_use] extern crate quick_error; pub use mapping_context::{MappingContext, MappingContextNewError, MappingContextNewWarning}; -pub use hole_punch_server_addr::HolePunchServerAddr; pub use mapped_socket_addr::MappedSocketAddr; pub use rendezvous_info::{PrivRendezvousInfo, PubRendezvousInfo, gen_rendezvous_info}; @@ -81,14 +74,12 @@ pub use mapped_tcp_socket::{MappedTcpSocket, tcp_punch_hole}; pub use simple_udp_hole_punch_server::{SimpleUdpHolePunchServer, SimpleUdpHolePunchServerNewError}; mod mapping_context; -mod hole_punch_server_addr; mod mapped_socket_addr; mod rendezvous_info; mod mapped_udp_socket; mod punched_udp_socket; mod mapped_tcp_socket; mod simple_udp_hole_punch_server; -mod periodic_sender; mod socket_utils; mod listener_message; diff --git a/src/mapped_tcp_socket.rs b/src/mapped_tcp_socket.rs index a0c16871..16e073e9 100644 --- a/src/mapped_tcp_socket.rs +++ b/src/mapped_tcp_socket.rs @@ -40,22 +40,22 @@ impl MappedTcpSocket { /// Map an existing tcp socket. The socket must not bound or connected. This /// function will set the options to make the socket address reuseable /// before binding it. - pub fn map(socket: net2::TcpBuilder, mc: &MappingContext) + pub fn map(_socket: net2::TcpBuilder, _mc: &MappingContext) -> MappedTcpSocket { unimplemented!(); } /// Create a new `MappedTcpSocket` - pub fn new(mc: &MappingContext) -> MappedTcpSocket { + pub fn new(_mc: &MappingContext) -> MappedTcpSocket { unimplemented!(); } } /// Perform a tcp rendezvous connect. `socket` should have been obtained from a /// `MappedTcpSocket`. -pub fn tcp_punch_hole(socket: net2::TcpBuilder, - our_priv_rendezvous_info: PrivRendezvousInfo, - their_pub_rendezvous_info: PubRendezvousInfo) +pub fn tcp_punch_hole(_socket: net2::TcpBuilder, + _our_priv_rendezvous_info: PrivRendezvousInfo, + _their_pub_rendezvous_info: PubRendezvousInfo) -> TcpStream { unimplemented!(); } diff --git a/src/mapped_udp_socket.rs b/src/mapped_udp_socket.rs index 456acbd6..cebb22d9 100644 --- a/src/mapped_udp_socket.rs +++ b/src/mapped_udp_socket.rs @@ -26,18 +26,15 @@ use std::collections::HashSet; use igd; use time; -use get_if_addrs; use ip::{SocketAddrExt, IpAddr}; use maidsafe_utilities::serialisation::{deserialise, serialise}; use socket_addr::SocketAddr; use w_result::{WResult, WOk, WErr}; -use hole_punch_server_addr::HolePunchServerAddr; use listener_message; use mapping_context; use mapping_context::MappingContext; use mapped_socket_addr::MappedSocketAddr; -use periodic_sender::PeriodicSender; use socket_utils; use socket_utils::RecvUntil; @@ -266,13 +263,12 @@ impl MappedUdpSocket { let send_data = listener_message::REQUEST_MAGIC_CONSTANT; let mut simple_servers: HashSet = mapping_context::simple_servers(&mc) .into_iter().collect(); - let mut deadline = time::SteadyTime::now(); + // Ping all the simple servers and waiting for a response. - // Run this loop at most 8 times for a maximum timeout of 250ms * 8 == 2 seconds. - let mut attempt = 0; - let mut max_attempts = 8; - while attempt < max_attempts && simple_servers.len() > 0 { - attempt += 1; + let start_time = time::SteadyTime::now(); + let mut deadline = start_time; + let mut final_deadline = start_time + time::Duration::seconds(2); + while deadline < final_deadline && simple_servers.len() > 0 { deadline = deadline + time::Duration::milliseconds(250); // TODO(canndrew): We should limit the number of servers that we send to. If the user @@ -304,13 +300,13 @@ impl MappedUdpSocket { // give us the same address. By contrast, servers on the same subnet as us or // behind the same carrier-level NAT are likely to respond in under a second. // So once we have one global address drop the timeout. - // TODO(canndrew): For now this is commented-out. Waiting for the is_global - // method to become available in the next stable rust. - /* - if recv_addr.ip().is_global() { - max_attempts = 4; + + // TODO(canndrew): Use IpAddr::is_global when it's available + // let is_global = recv_addr.is_global(); + let is_global = false; + if is_global { + final_deadline = start_time + time::Duration::seconds(1); }; - */ // Add this endpoint if we don't already know about it. We may have found it // through IGD or it may be a local interface. diff --git a/src/mapping_context.rs b/src/mapping_context.rs index eeaeb19a..470100f6 100644 --- a/src/mapping_context.rs +++ b/src/mapping_context.rs @@ -20,21 +20,16 @@ use std::sync::RwLock; use std::io; -use std::fmt; use std::net::{Ipv4Addr, Ipv6Addr}; -use std; use std::thread; use std::time::Duration; -use ip::IpAddr; use igd; use socket_addr::SocketAddr; use w_result::{WResult, WOk, WErr}; use get_if_addrs; use void::Void; -use hole_punch_server_addr::HolePunchServerAddr; - /// You need to create a `MappingContext` before doing any socket mapping. This /// `MappingContext` should ideally be kept throughout the lifetime of the /// program. Internally it caches a addresses of UPnP servers and hole punching diff --git a/src/periodic_sender.rs b/src/periodic_sender.rs deleted file mode 100644 index 209c7b78..00000000 --- a/src/periodic_sender.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2015 MaidSafe.net limited. -// -// This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, -// version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which -// licence you accepted on initial access to the Software (the "Licences"). -// -// By contributing code to the SAFE Network Software, or to this project generally, you agree to be -// bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the -// Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. -// -// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed -// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. -// -// Please review the Licences for the specific language governing permissions and limitations -// relating to use of the SAFE Network Software. - -use std::net::UdpSocket; -use socket_addr::SocketAddr; - -#[must_use] -pub struct PeriodicSender { - notify_exit: ::std::sync::mpsc::Sender<()>, - join_guard: ::crossbeam::ScopedJoinHandle<()>, - payload_sender: ::std::sync::mpsc::Sender, - destination_sender: ::std::sync::mpsc::Sender, /* join_guard: ::crossbeam::ScopedJoinHandle>, */ -} - -impl<'a, 'b: 'a, D: AsRef<[u8]> + Send + 'b> PeriodicSender { - pub fn start(udp_socket: UdpSocket, - destination: SocketAddr, - scope: &::crossbeam::Scope<'a>, - data: D, - period: ::std::time::Duration) - -> PeriodicSender { - let (tx, rx) = ::std::sync::mpsc::channel::<()>(); - let (payload_tx, payload_rx) = ::std::sync::mpsc::channel::(); - let (destination_tx, destination_rx) = ::std::sync::mpsc::channel::(); - let join_guard = scope.spawn(move || { - let mut data = data; - let mut destination = destination; - loop { - // TODO (canndrew): Will be possible to extract this error through `stop()` - // once the rust guys implement linear types/disableable drop. - // see: https://github.com/rust-lang/rfcs/issues/523 - // see: https://github.com/rust-lang/rfcs/issues/814 - let _ = udp_socket.send_to(data.as_ref(), &*destination); - ::std::thread::park_timeout(period); - match rx.try_recv() { - Err(::std::sync::mpsc::TryRecvError::Empty) => (), - Err(::std::sync::mpsc::TryRecvError::Disconnected) => panic!(), - Ok(()) => return, - } - match payload_rx.try_recv() { - Err(::std::sync::mpsc::TryRecvError::Empty) => (), - Err(::std::sync::mpsc::TryRecvError::Disconnected) => panic!(), - Ok(d) => data = d, - } - match destination_rx.try_recv() { - Err(::std::sync::mpsc::TryRecvError::Empty) => (), - Err(::std::sync::mpsc::TryRecvError::Disconnected) => panic!(), - Ok(a) => destination = a, - } - } - }); - PeriodicSender { - notify_exit: tx, - join_guard: join_guard, - payload_sender: payload_tx, - destination_sender: destination_tx, - } - } - - pub fn set_payload(&self, data: D) { - // ignore this error, indicates that the sending thread has died - let _ = self.payload_sender.send(data); - } - - pub fn set_destination(&self, destination: SocketAddr) { - // ignore this error, indicates that the sending thread has died - let _ = self.destination_sender.send(destination); - } - - // pub fn stop(self) -> io::Result<()> { - // self.notify_exit.send(()); - // self.join_guard.thread().unpark(); - // self.join_guard.join() - // } - // -} - -impl Drop for PeriodicSender { - fn drop(&mut self) { - self.notify_exit.send(()).unwrap(); - self.join_guard.thread().unpark(); - } -} diff --git a/src/punched_udp_socket.rs b/src/punched_udp_socket.rs index cd7de43c..1943d66c 100644 --- a/src/punched_udp_socket.rs +++ b/src/punched_udp_socket.rs @@ -23,10 +23,11 @@ use std::net::UdpSocket; use std; use std::thread; +use cbor; use time; use socket_addr::SocketAddr; +use w_result::{WResult, WOk, WErr}; -use periodic_sender::PeriodicSender; use rendezvous_info::{PrivRendezvousInfo, PubRendezvousInfo}; use rendezvous_info; use socket_utils::RecvUntil; @@ -37,6 +38,12 @@ struct HolePunch { pub ack: bool, } +/// Used for reporting warnings inside `UdpPunchHoleWarning` +#[derive(Debug)] +pub struct HolePunchPacketData { + data: HolePunch, +} + /// A udp socket that has been hole punched. pub struct PunchedUdpSocket { /// The UDP socket. @@ -45,14 +52,76 @@ pub struct PunchedUdpSocket { pub peer_addr: SocketAddr, } +quick_error! { + /// Warnings raise by `PunchedUdpSocket::punch_hole` + #[derive(Debug)] + #[allow(variant_size_differences)] + pub enum UdpPunchHoleWarning { + /// Received a hole punch packet that does correspond to the connection we are trying to + /// make. Possibly, hole punch packets from an unrelated connection or arriving on this socket. + UnexpectedHolePunchPacket { + hole_punch: HolePunchPacketData, + } { + description("Received a hole punch packet that does correspond to the \ + connection we are trying to make. Possibly, hole punch packets \ + from an unrelated connection or arriving on this socket.") + display("Received a hole punch packet that does correspond to the \ + connection we are trying to make. Possibly, hole punch packets \ + from an unrelated connection or arriving on this socket. Debug \ + info: {:#?}", hole_punch) + } + /// Received invalid data on the udp socket while hole punching. + InvalidHolePunchPacket { + err: cbor::CborError, + } { + description("Received invalid data on the udp socket while hole punching") + display("Received invalid data on the udp socket while hole punching. \ + cbor decoding produced the error: {}", err) + cause(err) + } + } +} + +quick_error! { + /// Error returned by PunchedUdpSocket::punch_hole + #[derive(Debug)] + pub enum UdpPunchHoleError { + /// Timed out waiting for a response from the peer. + TimedOut { + description("Timed out waiting for a response from the peer.") + } + /// IO error when using socket + Io { + err: io::Error, + } { + description("IO error when using socket") + display("IO error when using socket: {}", err) + cause(err) + } + } +} +impl From for io::Error { + fn from(err: UdpPunchHoleError) -> io::Error { + match err { + UdpPunchHoleError::TimedOut => io::Error::new(io::ErrorKind::TimedOut, + "Udp hole punching timed out \ + waiting for a response from \ + the peer"), + UdpPunchHoleError::Io { err } => err, + } + } +} impl PunchedUdpSocket { /// Punch a udp socket using a mapped socket and the peer's rendezvous info. - pub fn punch_hole(mut socket: UdpSocket, + pub fn punch_hole(socket: UdpSocket, our_priv_rendezvous_info: PrivRendezvousInfo, their_pub_rendezvous_info: PubRendezvousInfo) - -> io::Result { + -> WResult + { + let mut warnings = Vec::new(); + let (endpoints, their_secret) = rendezvous_info::decompose(their_pub_rendezvous_info); let our_secret @@ -136,7 +205,7 @@ impl PunchedUdpSocket { // TODO(canndrew): How should we handle partial write? let _ = match socket.send_to(&send_data[..], &**addr) { Ok(n) => n, - Err(e) => return Err(e), + Err(e) => return WErr(UdpPunchHoleError::Io { err: e }), }; } // Keep reading until it's time to send to all endpoints again. @@ -144,17 +213,17 @@ impl PunchedUdpSocket { let (read_size, addr) = match socket.recv_until(&mut recv_data[..], deadline) { Ok(Some(x)) => x, Ok(None) => break, - Err(e) => return Err(e), + Err(e) => return WErr(UdpPunchHoleError::Io { err: e }), }; match ::cbor::Decoder::from_reader(&recv_data[..read_size]) .decode::() .next() { - Some(Ok(ref hp)) => { + Some(Ok(hp)) => { if hp.secret == our_secret && hp.ack { - return Ok(PunchedUdpSocket { + return WOk(PunchedUdpSocket { socket: socket, peer_addr: addr, - }); + }, warnings); } if hp.secret == their_secret { let send_data = { @@ -175,31 +244,34 @@ impl PunchedUdpSocket { // TODO(canndrew): handle partial writes. let _ = match socket.send_to(&send_data[..], &*addr) { Ok(n) => n, - Err(e) => return Err(e), + Err(e) => return WErr(UdpPunchHoleError::Io { err: e }), }; thread::sleep(std::time::Duration::from_millis(100)); let _ = match socket.send_to(&send_data[..], &*addr) { Ok(n) => n, - Err(e) => return Err(e), + Err(e) => return WErr(UdpPunchHoleError::Io { err: e }), }; - return Ok(PunchedUdpSocket { + return WOk(PunchedUdpSocket { socket: socket, peer_addr: addr, - }); - } - else { - // TODO(canndrew): report this error + }, warnings); } + warnings.push(UdpPunchHoleWarning::UnexpectedHolePunchPacket { + hole_punch: HolePunchPacketData { + data: hp, + }, + }); } Some(Err(e)) => { - // TODO(canndrew): report this error + warnings.push(UdpPunchHoleWarning::InvalidHolePunchPacket { + err: e, + }); } None => (), }; } } - Err(io::Error::new(io::ErrorKind::TimedOut, - "Timed out waiting for rendevous connection")) + WErr(UdpPunchHoleError::TimedOut) } } diff --git a/src/simple_udp_hole_punch_server.rs b/src/simple_udp_hole_punch_server.rs index ce7b41c4..2c9ec633 100644 --- a/src/simple_udp_hole_punch_server.rs +++ b/src/simple_udp_hole_punch_server.rs @@ -22,15 +22,11 @@ use std::io; use std::net; use std::net::UdpSocket; use std::time::Duration; -use std::sync::mpsc::Sender; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; -use get_if_addrs; use maidsafe_utilities::serialisation::{deserialise, serialise}; use maidsafe_utilities::thread::RaiiThreadJoiner; -use rand; -use sodiumoxide::crypto::sign::PublicKey; use w_result::{WResult, WOk, WErr}; use socket_addr::SocketAddr; @@ -44,7 +40,8 @@ const UDP_READ_TIMEOUT_SECS: u64 = 2; /// RAII type for a hole punch server which speaks the simple hole punching protocol. pub struct SimpleUdpHolePunchServer<'a> { - mapping_context: &'a MappingContext, + // TODO(canndrew): Use this to refresh our external addrs. + _mapping_context: &'a MappingContext, stop_flag: Arc, _raii_joiner: RaiiThreadJoiner, known_endpoints: Vec, @@ -103,7 +100,7 @@ impl<'a> SimpleUdpHolePunchServer<'a> { })); WOk(SimpleUdpHolePunchServer { - mapping_context: mapping_context, + _mapping_context: mapping_context, stop_flag: stop_flag, _raii_joiner: raii_joiner, known_endpoints: mapped_socket.endpoints, diff --git a/src/socket_utils.rs b/src/socket_utils.rs index aa70ddfd..1ed231c8 100644 --- a/src/socket_utils.rs +++ b/src/socket_utils.rs @@ -19,7 +19,7 @@ use std::io; use std::net::{UdpSocket, Ipv4Addr, Ipv6Addr}; use socket_addr::SocketAddr; use std::io::ErrorKind; -use net2::TcpBuilder; +//use net2::TcpBuilder; /// A self interruptable receive trait that allows a timed-out period to be defined pub trait RecvUntil { @@ -90,6 +90,7 @@ pub fn ipv6_is_unspecified(addr: &Ipv6Addr) -> bool { addr.segments() == [0, 0, 0, 0, 0, 0, 0, 0] } +/* #[cfg(target_family = "unix")] #[allow(unsafe_code)] pub fn enable_so_reuseport(sock: &TcpBuilder) -> io::Result<()> { @@ -116,3 +117,5 @@ pub fn enable_so_reuseport(sock: &TcpBuilder) -> io::Result<()> { pub fn enable_so_reuseport(sock: &TcpBuilder) -> io::Result<()> { Ok(()) } +*/ + From 2f9f70f54ffdcd490639eacdcfda6074f8b62e92 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 11 Feb 2016 18:05:07 +0800 Subject: [PATCH 2/4] Small security fix in punch_hole --- src/punched_udp_socket.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/punched_udp_socket.rs b/src/punched_udp_socket.rs index 1943d66c..37fee17a 100644 --- a/src/punched_udp_socket.rs +++ b/src/punched_udp_socket.rs @@ -256,16 +256,22 @@ impl PunchedUdpSocket { peer_addr: addr, }, warnings); } - warnings.push(UdpPunchHoleWarning::UnexpectedHolePunchPacket { - hole_punch: HolePunchPacketData { - data: hp, - }, - }); + // Protect against a malicious peer sending us loads of spurious data. + if warnings.len() < 10 { + warnings.push(UdpPunchHoleWarning::UnexpectedHolePunchPacket { + hole_punch: HolePunchPacketData { + data: hp, + }, + }); + } } Some(Err(e)) => { - warnings.push(UdpPunchHoleWarning::InvalidHolePunchPacket { - err: e, - }); + // Protect against a malicious peer sending us loads of spurious data. + if warnings.len() < 10 { + warnings.push(UdpPunchHoleWarning::InvalidHolePunchPacket { + err: e, + }); + } } None => (), }; From fc0b63dd0a1c2a8ef4fecf7199945ea1d170a348 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 11 Feb 2016 19:16:54 +0800 Subject: [PATCH 3/4] Fix cargo test --- examples/udp-hole-punch.rs | 9 ++++-- src/lib.rs | 2 +- src/punched_udp_socket.rs | 58 +++++++++++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/examples/udp-hole-punch.rs b/examples/udp-hole-punch.rs index 4158dae4..8dd79ce8 100644 --- a/examples/udp-hole-punch.rs +++ b/examples/udp-hole-punch.rs @@ -171,8 +171,13 @@ fn main() { // Now we use the socket, our private rendezvous info and their public rendezvous info to // complete the connection. let punched_socket = match PunchedUdpSocket::punch_hole(socket, our_priv_info, their_pub_info) { - Ok(punched_socket) => punched_socket, - Err(e) => { + WOk(punched_socket, warnings) => { + for warning in warnings { + println!("Warning when punching hole: {}", warning); + } + punched_socket + }, + WErr(e) => { println!("IO error punching udp socket: {}", e); println!("Exiting."); return; diff --git a/src/lib.rs b/src/lib.rs index 143d0810..f28dda23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,7 +69,7 @@ pub use rendezvous_info::{PrivRendezvousInfo, PubRendezvousInfo, gen_rendezvous_info}; pub use mapped_udp_socket::{MappedUdpSocket, MappedUdpSocketMapError, MappedUdpSocketMapWarning, MappedUdpSocketNewError}; -pub use punched_udp_socket::PunchedUdpSocket; +pub use punched_udp_socket::{PunchedUdpSocket, filter_udp_hole_punch_packet}; pub use mapped_tcp_socket::{MappedTcpSocket, tcp_punch_hole}; pub use simple_udp_hole_punch_server::{SimpleUdpHolePunchServer, SimpleUdpHolePunchServerNewError}; diff --git a/src/punched_udp_socket.rs b/src/punched_udp_socket.rs index 37fee17a..37cc50b8 100644 --- a/src/punched_udp_socket.rs +++ b/src/punched_udp_socket.rs @@ -281,6 +281,21 @@ impl PunchedUdpSocket { } } +/// Returns `None` if `data` looks like a hole punching message. Otherwise returns the data it was +/// given. +/// +/// Punching a hole with a udp socket involves packets being sent and received on the socket. After +/// hole punching succeeds it's possible that more hole punching packets sent by the remote peer +/// may yet arrive on the socket. This function can be used to filter out those packets. +pub fn filter_udp_hole_punch_packet(data: &[u8]) -> Option<&[u8]> { + match ::cbor::Decoder::from_reader(data) + .decode::().next() + { + Some(Ok(_)) => None, + _ => Some(data), + } +} + #[cfg(test)] mod tests { use std::sync::mpsc; @@ -290,9 +305,10 @@ mod tests { use mapping_context::MappingContext; use mapped_udp_socket::MappedUdpSocket; - use punched_udp_socket::PunchedUdpSocket; + use punched_udp_socket::{PunchedUdpSocket, filter_udp_hole_punch_packet}; use rendezvous_info::gen_rendezvous_info; + #[test] fn two_peers_punch_hole_over_loopback() { let mapping_context = unwrap_result!(MappingContext::new().result_discard()); let mapped_socket_0 = unwrap_result!(MappedUdpSocket::new(&mapping_context).result_discard()); @@ -310,36 +326,52 @@ mod tests { let res = PunchedUdpSocket::punch_hole(socket_0, priv_info_0, pub_info_1); - tx_0.send(res); + unwrap_result!(tx_0.send(res)); }); let jh_1 = thread!("two_peers_hole_punch_over_loopback punch socket 1", move || { let res = PunchedUdpSocket::punch_hole(socket_1, priv_info_1, pub_info_0); - tx_1.send(res); + unwrap_result!(tx_1.send(res)); }); thread::sleep(Duration::from_millis(500)); - let punched_socket_0 = unwrap_result!(unwrap_result!(rx_0.try_recv())); - let punched_socket_1 = unwrap_result!(unwrap_result!(rx_1.try_recv())); + let punched_socket_0 = unwrap_result!(unwrap_result!(rx_0.try_recv()).result_discard()); + let punched_socket_1 = unwrap_result!(unwrap_result!(rx_1.try_recv()).result_discard()); const DATA_LEN: usize = 8; let data_send: [u8; DATA_LEN] = rand::random(); let mut data_recv; // Send data from 0 to 1 - data_recv = [0u8; DATA_LEN]; - punched_socket_0.socket.send_to(&data_send[..], &*punched_socket_0.peer_addr); - let (n, _) = unwrap_result!(punched_socket_1.socket.recv_from(&mut data_recv[..])); + data_recv = [0u8; 1024]; + let n = unwrap_result!(punched_socket_0.socket.send_to(&data_send[..], &*punched_socket_0.peer_addr)); assert_eq!(n, DATA_LEN); - assert_eq!(data_send, data_recv); + loop { + let (n, _) = unwrap_result!(punched_socket_1.socket.recv_from(&mut data_recv[..])); + match filter_udp_hole_punch_packet(&data_recv[..n]) { + Some(d) => { + assert_eq!(data_send, d); + break; + }, + None => continue, + } + } // Send data from 1 to 0 - data_recv = [0u8; DATA_LEN]; - punched_socket_1.socket.send_to(&data_send[..], &*punched_socket_1.peer_addr); - let (n, _) = unwrap_result!(punched_socket_0.socket.recv_from(&mut data_recv[..])); + data_recv = [0u8; 1024]; + let n = unwrap_result!(punched_socket_1.socket.send_to(&data_send[..], &*punched_socket_1.peer_addr)); assert_eq!(n, DATA_LEN); - assert_eq!(data_send, data_recv); + loop { + let (n, _) = unwrap_result!(punched_socket_0.socket.recv_from(&mut data_recv[..])); + match filter_udp_hole_punch_packet(&data_recv[..n]) { + Some(d) => { + assert_eq!(data_send, d); + break; + }, + None => continue, + } + } unwrap_result!(jh_0.join()); unwrap_result!(jh_1.join()); From a6d83c2fef92254a35d2fda0267415458a20b06d Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Mon, 15 Feb 2016 12:58:39 +0800 Subject: [PATCH 4/4] Fix errors after rebase. --- src/mapped_udp_socket.rs | 2 +- src/simple_udp_hole_punch_server.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mapped_udp_socket.rs b/src/mapped_udp_socket.rs index cebb22d9..8e44ea0d 100644 --- a/src/mapped_udp_socket.rs +++ b/src/mapped_udp_socket.rs @@ -27,7 +27,7 @@ use std::collections::HashSet; use igd; use time; use ip::{SocketAddrExt, IpAddr}; -use maidsafe_utilities::serialisation::{deserialise, serialise}; +use maidsafe_utilities::serialisation::deserialise; use socket_addr::SocketAddr; use w_result::{WResult, WOk, WErr}; diff --git a/src/simple_udp_hole_punch_server.rs b/src/simple_udp_hole_punch_server.rs index 2c9ec633..59f286af 100644 --- a/src/simple_udp_hole_punch_server.rs +++ b/src/simple_udp_hole_punch_server.rs @@ -19,13 +19,12 @@ //! NAT traversal utilities. use std::io; -use std::net; use std::net::UdpSocket; use std::time::Duration; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; -use maidsafe_utilities::serialisation::{deserialise, serialise}; +use maidsafe_utilities::serialisation::serialise; use maidsafe_utilities::thread::RaiiThreadJoiner; use w_result::{WResult, WOk, WErr};