use std::sync::RwLock;
use std::io;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::thread;
use std::time::Duration;
use igd;
use socket_addr::SocketAddr;
use w_result::{WResult, WOk, WErr};
use get_if_addrs;
use void::Void;
use socket_utils;
pub struct MappingContext {
interfaces_v4: RwLock<Vec<InterfaceV4>>,
interfaces_v6: RwLock<Vec<InterfaceV6>>,
simple_udp_servers: RwLock<Vec<SocketAddr>>,
simple_tcp_servers: RwLock<Vec<SocketAddr>>,
}
#[derive(Clone)]
pub struct InterfaceV4 {
pub gateway: Option<igd::Gateway>,
pub addr: Ipv4Addr,
}
#[derive(Clone)]
pub struct InterfaceV6 {
pub addr: Ipv6Addr,
}
quick_error! {
#[derive(Debug)]
pub enum MappingContextNewError {
ListInterfaces {
err:io::Error,
} {
description("Failed to list the local machine's network interfaces")
display("Failed to list the local machines's network interfaces \
get_if_addrs returned an error: {}", err)
cause(err)
}
SpawnThread {
err: io::Error
} {
description("Failed to spawn a thread")
display("Failed to spawn a thread. \
thread::spawn returned an error: {}", err)
cause(err)
}
}
}
impl From<MappingContextNewError> for io::Error {
fn from(e: MappingContextNewError) -> io::Error {
let err_str = format!("{}", e);
let kind = match e {
MappingContextNewError::ListInterfaces { err } => err.kind(),
MappingContextNewError::SpawnThread { err } => err.kind(),
};
io::Error::new(kind, err_str)
}
}
quick_error! {
#[derive(Debug)]
pub enum MappingContextNewWarning {
SearchGateway {
if_name: String,
if_addr: Ipv4Addr,
err: igd::SearchError
} {
description("Failed to find IGD gateway")
display("Failed to find an IGD gateway on network interface {} {}. \
igd::search_gateway_from_timeout returned an error: {}",
if_name, if_addr, err)
cause(err)
}
}
}
impl MappingContext {
pub fn new() -> WResult<MappingContext, MappingContextNewWarning, MappingContextNewError> {
let interfaces = match get_if_addrs::get_if_addrs() {
Ok(if_addrs) => if_addrs,
Err(e) => return WErr(MappingContextNewError::ListInterfaces { err: e }),
};
let mut interfaces_v4 = Vec::new();
let mut interfaces_v6 = Vec::new();
let mut warnings = Vec::new();
let mut search_threads = Vec::new();
for interface in interfaces {
let addr_v4 = match interface.addr {
get_if_addrs::IfAddr::V4(v4_addr) => {
v4_addr.ip
},
get_if_addrs::IfAddr::V6(v6_addr) => {
interfaces_v6.push(InterfaceV6 {
addr: v6_addr.ip,
});
continue;
},
};
if socket_utils::ipv4_is_loopback(&addr_v4) {
interfaces_v4.push(InterfaceV4 {
gateway: None,
addr: addr_v4,
});
continue;
};
let if_name = interface.name;
search_threads.push(thread::Builder::new()
.name(From::from("IGD search"))
.spawn(move || -> WResult<_, _, Void> {
let mut warnings = Vec::new();
let gateway = match igd::search_gateway_from_timeout(addr_v4, Duration::from_secs(1)) {
Ok(gateway) => Some(gateway),
Err(e) => {
warnings.push(MappingContextNewWarning::SearchGateway {
if_name: if_name,
if_addr: addr_v4,
err: e,
});
None
},
};
WOk(InterfaceV4 {
gateway: gateway,
addr: addr_v4,
}, warnings)
}));
};
for search_thread in search_threads {
match search_thread {
Err(e) => return WErr(MappingContextNewError::SpawnThread { err: e }),
Ok(jh) => {
let res = unwrap_result!(jh.join());
match res {
WErr(e) => match e {},
WOk(interface, ws) => {
interfaces_v4.push(interface);
warnings.extend(ws);
}
}
}
}
}
let mc = MappingContext {
interfaces_v4: RwLock::new(interfaces_v4),
interfaces_v6: RwLock::new(interfaces_v6),
simple_udp_servers: RwLock::new(Vec::new()),
simple_tcp_servers: RwLock::new(Vec::new()),
};
WOk(mc, warnings)
}
pub fn add_simple_udp_servers<S>(&self, servers: S)
where S: IntoIterator<Item=SocketAddr>
{
let mut s = unwrap_result!(self.simple_udp_servers.write());
s.extend(servers)
}
pub fn add_simple_tcp_servers<S>(&self, servers: S)
where S: IntoIterator<Item=SocketAddr>
{
let mut s = unwrap_result!(self.simple_tcp_servers.write());
s.extend(servers)
}
}
pub fn interfaces_v4(mc: &MappingContext) -> Vec<InterfaceV4> {
unwrap_result!(mc.interfaces_v4.read()).clone()
}
pub fn interfaces_v6(mc: &MappingContext) -> Vec<InterfaceV6> {
unwrap_result!(mc.interfaces_v6.read()).clone()
}
pub fn simple_udp_servers(mc: &MappingContext) -> Vec<SocketAddr> {
unwrap_result!(mc.simple_udp_servers.read()).clone()
}
pub fn simple_tcp_servers(mc: &MappingContext) -> Vec<SocketAddr> {
unwrap_result!(mc.simple_tcp_servers.read()).clone()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_mapping_context() {
let _ = unwrap_result!(MappingContext::new().result_discard());
}
}