use std::io;
use std::net::UdpSocket;
use std::net;
use std::net::IpAddr;
use std::time::{Instant, Duration};
use std::collections::HashSet;
use igd;
use maidsafe_utilities::serialisation::deserialise;
use socket_addr::SocketAddr;
use w_result::{WResult, WOk, WErr};
use listener_message;
use mapping_context;
use mapping_context::MappingContext;
use mapped_socket_addr::MappedSocketAddr;
use socket_utils;
use socket_utils::RecvUntil;
pub struct MappedUdpSocket {
pub socket: UdpSocket,
pub endpoints: Vec<MappedSocketAddr>
}
quick_error! {
#[derive(Debug)]
pub enum MappedUdpSocketMapError {
SocketLocalAddr {
err: io::Error
} {
description("Error getting local address of socket \
(have you called bind() on the socket?)")
display("Error getting local address of socket. \
UdpSocket::local_addr returned an error: {}
(have you called bind() on the socket?).",
err)
cause(err)
}
RecvError {
err: io::Error
} {
description("IO error receiving data on socket")
display("IO error receiving data on socket: {}", err)
cause(err)
}
SendError {
err: io::Error
} {
description("IO error sending data on socket")
display("IO error sending data on socket: {}", err)
cause(err)
}
}
}
impl From<MappedUdpSocketMapError> for io::Error {
fn from(e: MappedUdpSocketMapError) -> io::Error {
let err_str = format!("{}", e);
let kind = match e {
MappedUdpSocketMapError::SocketLocalAddr { err } => err.kind(),
MappedUdpSocketMapError::RecvError { err } => err.kind(),
MappedUdpSocketMapError::SendError { err } => err.kind(),
};
io::Error::new(kind, err_str)
}
}
quick_error! {
#[derive(Debug)]
pub enum MappedUdpSocketMapWarning {
FindGateway {
err: igd::SearchError
} {
description("Error searching for IGD gateway")
display("Error searching for IGD gateway. \
igd::search_gateway_from_timeout returned an error: {}",
err)
cause(err)
}
GetExternalPort {
gateway_addr: net::SocketAddrV4,
err: igd::AddAnyPortError,
} {
description("Error mapping external address and port through IGD \
gateway")
display("Error mapping external address and port through IGD \
gateway at address {}. igd::Gateway::get_any_address \
returned an error: {}", gateway_addr, err)
cause(err)
}
}
}
quick_error! {
#[derive(Debug)]
pub enum MappedUdpSocketNewError {
CreateSocket {
err: io::Error
} {
description("Error creating a new udp socket bound to 0.0.0.0:0")
display("Error creating a new udp socket bound to 0.0.0.0:0. \
UdpSocket::bind returned an IO error: {}", err)
cause(err)
}
MapSocket {
err: MappedUdpSocketMapError
} {
description("Error mapping udp socket")
display("Error mapping udp socket. MappedUdpSocket::map returned \
an error: {}", err)
cause(err)
}
}
}
impl From<MappedUdpSocketNewError> for io::Error {
fn from(e: MappedUdpSocketNewError) -> io::Error {
let err_str = format!("{}", e);
let kind = match e {
MappedUdpSocketNewError::CreateSocket { err } => err.kind(),
MappedUdpSocketNewError::MapSocket { err } => {
let err: io::Error = From::from(err);
err.kind()
},
};
io::Error::new(kind, err_str)
}
}
impl MappedUdpSocket {
pub fn map(socket: UdpSocket, mc: &MappingContext, deadline: Instant)
-> WResult<MappedUdpSocket, MappedUdpSocketMapWarning, MappedUdpSocketMapError>
{
let mut endpoints = Vec::new();
let mut warnings = Vec::new();
let local_addr = match socket.local_addr() {
Ok(local_addr) => local_addr,
Err(e) => return WErr(MappedUdpSocketMapError::SocketLocalAddr { err: e })
};
match local_addr.ip() {
IpAddr::V4(ipv4_addr) => {
if socket_utils::ipv4_is_unspecified(&ipv4_addr) {
for iface_v4 in mapping_context::interfaces_v4(&mc) {
let local_iface_addr = net::SocketAddrV4::new(iface_v4.addr, local_addr.port());
endpoints.push(MappedSocketAddr {
addr: SocketAddr(net::SocketAddr::V4(local_iface_addr)),
nat_restricted: false,
});
if let Some(gateway) = iface_v4.gateway {
match gateway.get_any_address(igd::PortMappingProtocol::UDP,
local_iface_addr, 0,
"rust nat_traversal")
{
Ok(external_addr) => {
endpoints.push(MappedSocketAddr {
addr: SocketAddr(net::SocketAddr::V4(external_addr)),
nat_restricted: false,
});
},
Err(e) => {
warnings.push(MappedUdpSocketMapWarning::GetExternalPort {
gateway_addr: gateway.addr,
err: e,
});
}
}
};
};
}
else {
let local_addr_v4 = net::SocketAddrV4::new(ipv4_addr, local_addr.port());
endpoints.push(MappedSocketAddr {
addr: SocketAddr(net::SocketAddr::V4(local_addr_v4)),
nat_restricted: false,
});
let mut gateway_opt_opt = None;
for iface_v4 in mapping_context::interfaces_v4(&mc) {
if iface_v4.addr == ipv4_addr {
gateway_opt_opt = Some(iface_v4.gateway);
break;
}
};
let gateway_opt = match gateway_opt_opt {
Some(gateway_opt) => gateway_opt,
None => {
match igd::search_gateway_from_timeout(ipv4_addr, Duration::from_secs(1)) {
Ok(gateway) => Some(gateway),
Err(e) => {
warnings.push(MappedUdpSocketMapWarning::FindGateway {
err: e
});
None
}
}
}
};
if let Some(gateway) = gateway_opt {
match gateway.get_any_address(igd::PortMappingProtocol::UDP,
local_addr_v4, 0,
"rust nat_traversal")
{
Ok(external_addr) => {
endpoints.push(MappedSocketAddr {
addr: SocketAddr(net::SocketAddr::V4(external_addr)),
nat_restricted: false,
});
},
Err(e) => {
warnings.push(MappedUdpSocketMapWarning::GetExternalPort {
gateway_addr: gateway.addr,
err: e,
});
}
}
};
};
},
IpAddr::V6(ipv6_addr) => {
if socket_utils::ipv6_is_unspecified(&ipv6_addr) {
for iface_v6 in mapping_context::interfaces_v6(&mc) {
let local_iface_addr = net::SocketAddr::V6(net::SocketAddrV6::new(iface_v6.addr, local_addr.port(), 0, 0));
endpoints.push(MappedSocketAddr {
addr: SocketAddr(local_iface_addr),
nat_restricted: false,
});
};
}
else {
endpoints.push(MappedSocketAddr {
addr: SocketAddr(net::SocketAddr::V6(net::SocketAddrV6::new(ipv6_addr, local_addr.port(), 0, 0))),
nat_restricted: false,
});
}
},
};
const MAX_DATAGRAM_SIZE: usize = 256;
let send_data = listener_message::REQUEST_MAGIC_CONSTANT;
let mut simple_servers: HashSet<SocketAddr> = mapping_context::simple_udp_servers(&mc)
.into_iter().collect();
let start_time = Instant::now();
let mut recv_deadline = start_time;
let mut deadline = deadline;
while recv_deadline < deadline && simple_servers.len() > 0 {
recv_deadline = recv_deadline + Duration::from_millis(250);
for simple_server in &simple_servers {
let _ = match socket.send_to(&send_data[..], &**simple_server) {
Ok(n) => n,
Err(e) => return WErr(MappedUdpSocketMapError::SendError { err: e }),
};
};
let mut recv_data = [0u8; MAX_DATAGRAM_SIZE];
loop {
let (read_size, recv_addr) = match socket.recv_until(&mut recv_data[..], recv_deadline) {
Ok(Some(res)) => res,
Ok(None) => break,
Err(e) => return WErr(MappedUdpSocketMapError::RecvError { err: e }),
};
if let Ok(listener_message::EchoExternalAddr { external_addr }) =
deserialise::<listener_message::EchoExternalAddr>(&recv_data[..read_size]) {
simple_servers.remove(&recv_addr);
let is_global = false;
if is_global {
let now = Instant::now();
if deadline > now {
deadline = now + (now - deadline) / 2;
}
};
if endpoints.iter().all(|e| e.addr != external_addr) {
endpoints.push(MappedSocketAddr {
addr: external_addr,
nat_restricted: true,
});
}
}
}
}
WOk(MappedUdpSocket {
socket: socket,
endpoints: endpoints,
}, warnings)
}
pub fn new(mc: &MappingContext, deadline: Instant)
-> WResult<MappedUdpSocket, MappedUdpSocketMapWarning, MappedUdpSocketNewError>
{
let mut attempt = 0;
'attempt: loop {
attempt += 1;
let socket = match UdpSocket::bind("0.0.0.0:0") {
Ok(socket) => socket,
Err(e) => return WErr(MappedUdpSocketNewError::CreateSocket { err: e }),
};
let (socket, warnings) = match Self::map(socket, mc, deadline) {
WOk(s, ws) => (s, ws),
WErr(e) => return WErr(MappedUdpSocketNewError::MapSocket { err: e }),
};
if attempt < 3 {
for warning in &warnings {
match *warning {
MappedUdpSocketMapWarning::GetExternalPort {
err: igd::AddAnyPortError::ExternalPortInUse,
..
} => continue 'attempt,
_ => (),
}
}
}
return WOk(socket, warnings);
}
}
}