use std::io;
use std::net::UdpSocket;
use std::time::{Instant, Duration};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use maidsafe_utilities::serialisation::serialise;
use maidsafe_utilities::thread::RaiiThreadJoiner;
use w_result::{WResult, WOk, WErr};
use socket_addr::SocketAddr;
use listener_message;
use mapping_context::MappingContext;
use mapped_udp_socket::{MappedUdpSocket, MappedUdpSocketNewError, MappedUdpSocketMapWarning};
const UDP_READ_TIMEOUT_SECS: u64 = 2;
pub struct SimpleUdpHolePunchServer<T: AsRef<MappingContext>> {
_mapping_context: T,
stop_flag: Arc<AtomicBool>,
_raii_joiner: RaiiThreadJoiner,
known_endpoints: Vec<SocketAddr>,
}
quick_error! {
#[derive(Debug)]
pub enum SimpleUdpHolePunchServerNewError {
CreateMappedSocket {
err: MappedUdpSocketNewError } {
description("Error creating a mapped udp socket to listen on.")
display("Error creating a mapped udp socket to listen on: {}", err)
cause(err)
}
SetSocketTimeout {
err: io::Error
} {
description("Error setting the timeout on the server's listening socket.")
display("Error setting the timeout on the server's listening socket: {}.", err)
cause(err)
}
}
}
impl From<SimpleUdpHolePunchServerNewError> for io::Error {
fn from(e: SimpleUdpHolePunchServerNewError) -> io::Error {
let err_str = format!("{}", e);
let kind = match e {
SimpleUdpHolePunchServerNewError::CreateMappedSocket { err } => {
let err: io::Error = From::from(err);
err.kind()
},
SimpleUdpHolePunchServerNewError::SetSocketTimeout { err } => err.kind(),
};
io::Error::new(kind, err_str)
}
}
impl<T: AsRef<MappingContext>> SimpleUdpHolePunchServer<T> {
pub fn new(mapping_context: T, deadline: Instant)
-> WResult<SimpleUdpHolePunchServer<T>,
MappedUdpSocketMapWarning,
SimpleUdpHolePunchServerNewError>
{
let (mapped_socket, warnings) = match MappedUdpSocket::new(mapping_context.as_ref(), deadline) {
WOk(mapped_socket, warnings) => (mapped_socket, warnings),
WErr(e) => {
return WErr(SimpleUdpHolePunchServerNewError::CreateMappedSocket { err: e });
}
};
let udp_socket = mapped_socket.socket;
let stop_flag = Arc::new(AtomicBool::new(false));
let cloned_stop_flag = stop_flag.clone();
match udp_socket.set_read_timeout(Some(Duration::from_secs(UDP_READ_TIMEOUT_SECS))) {
Ok(()) => (),
Err(e) => {
return WErr(SimpleUdpHolePunchServerNewError::SetSocketTimeout { err: e })
}
};
let raii_joiner = RaiiThreadJoiner::new(thread!("SimpleUdpHolePunchServer", move || {
Self::run(udp_socket, cloned_stop_flag);
}));
let unrestricted_endpoints = mapped_socket.endpoints.into_iter().filter_map(|msa| {
match msa.nat_restricted {
false => Some(msa.addr),
true => None,
}
}).collect();
WOk(SimpleUdpHolePunchServer {
_mapping_context: mapping_context,
stop_flag: stop_flag,
_raii_joiner: raii_joiner,
known_endpoints: unrestricted_endpoints,
}, warnings)
}
fn run(udp_socket: UdpSocket,
stop_flag: Arc<AtomicBool>) {
let mut read_buf = [0; 1024];
while !stop_flag.load(Ordering::SeqCst) {
if let Ok((bytes_read, peer_addr)) = udp_socket.recv_from(&mut read_buf) {
if read_buf[..bytes_read] != listener_message::REQUEST_MAGIC_CONSTANT {
continue;
}
let resp = listener_message::EchoExternalAddr {
external_addr: SocketAddr(peer_addr.clone()),
};
let _ = udp_socket.send_to(&unwrap_result!(serialise(&resp)),
peer_addr);
}
}
}
pub fn addresses(&self) -> Vec<SocketAddr> {
self.known_endpoints.clone()
}
}
impl<T: AsRef<MappingContext>> Drop for SimpleUdpHolePunchServer<T> {
fn drop(&mut self) {
self.stop_flag.store(true, Ordering::SeqCst);
}
}