2018-06-29 14:12:26 -07:00
|
|
|
//! The `nat` module assists with NAT traversal
|
|
|
|
|
2018-06-29 16:49:23 -07:00
|
|
|
extern crate reqwest;
|
2018-06-29 14:12:26 -07:00
|
|
|
|
2018-08-28 16:32:40 -07:00
|
|
|
use pnet_datalink as datalink;
|
2018-06-28 11:28:28 -07:00
|
|
|
use rand::{thread_rng, Rng};
|
|
|
|
use std::io;
|
2018-08-28 16:32:40 -07:00
|
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
2018-06-29 14:12:26 -07:00
|
|
|
|
|
|
|
/// A data type representing a public Udp socket
|
|
|
|
pub struct UdpSocketPair {
|
|
|
|
pub addr: SocketAddr, // Public address of the socket
|
|
|
|
pub receiver: UdpSocket, // Locally bound socket that can receive from the public address
|
|
|
|
pub sender: UdpSocket, // Locally bound socket to send via public address
|
|
|
|
}
|
|
|
|
|
2018-06-29 16:49:23 -07:00
|
|
|
/// Tries to determine the public IP address of this machine
|
|
|
|
pub fn get_public_ip_addr() -> Result<IpAddr, String> {
|
|
|
|
let body = reqwest::get("http://ifconfig.co/ip")
|
|
|
|
.map_err(|err| err.to_string())?
|
|
|
|
.text()
|
|
|
|
.map_err(|err| err.to_string())?;
|
|
|
|
|
|
|
|
match body.lines().next() {
|
|
|
|
Some(ip) => Result::Ok(ip.parse().unwrap()),
|
|
|
|
None => Result::Err("Empty response body".to_string()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-28 16:32:40 -07:00
|
|
|
pub fn parse_port_or_addr(optstr: Option<&str>, default_port: u16) -> SocketAddr {
|
|
|
|
let daddr: SocketAddr = format!("0.0.0.0:{}", default_port)
|
|
|
|
.parse()
|
|
|
|
.expect("default socket address");
|
|
|
|
if let Some(addrstr) = optstr {
|
|
|
|
if let Ok(port) = addrstr.parse() {
|
|
|
|
let mut addr = daddr;
|
|
|
|
addr.set_port(port);
|
|
|
|
addr
|
|
|
|
} else if let Ok(addr) = addrstr.parse() {
|
|
|
|
addr
|
|
|
|
} else {
|
|
|
|
daddr
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
daddr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_ip_addr() -> Option<IpAddr> {
|
|
|
|
for iface in datalink::interfaces() {
|
|
|
|
for p in iface.ips {
|
|
|
|
if !p.ip().is_loopback() && !p.ip().is_multicast() {
|
|
|
|
match p.ip() {
|
|
|
|
IpAddr::V4(addr) => {
|
|
|
|
if !addr.is_link_local() {
|
|
|
|
return Some(p.ip());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
IpAddr::V6(_addr) => {
|
|
|
|
// Select an ipv6 address if the config is selected
|
|
|
|
#[cfg(feature = "ipv6")]
|
|
|
|
{
|
|
|
|
return Some(p.ip());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2018-06-28 11:28:28 -07:00
|
|
|
|
2018-08-28 16:32:40 -07:00
|
|
|
pub fn bind_in_range(range: (u16, u16)) -> io::Result<UdpSocket> {
|
|
|
|
let (start, end) = range;
|
|
|
|
let mut tries_left = end - start;
|
|
|
|
loop {
|
2018-06-28 11:28:28 -07:00
|
|
|
let rand_port = thread_rng().gen_range(start, end);
|
|
|
|
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), rand_port);
|
|
|
|
|
|
|
|
match UdpSocket::bind(addr) {
|
|
|
|
Result::Ok(val) => break Result::Ok(val),
|
2018-08-28 16:32:40 -07:00
|
|
|
Result::Err(err) => if err.kind() != io::ErrorKind::AddrInUse || tries_left == 0 {
|
2018-06-28 11:28:28 -07:00
|
|
|
return Err(err);
|
|
|
|
},
|
|
|
|
}
|
2018-08-28 16:32:40 -07:00
|
|
|
tries_left -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use nat::parse_port_or_addr;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_port_or_addr() {
|
|
|
|
let p1 = parse_port_or_addr(Some("9000"), 1);
|
|
|
|
assert_eq!(p1.port(), 9000);
|
|
|
|
let p2 = parse_port_or_addr(Some("127.0.0.1:7000"), 1);
|
|
|
|
assert_eq!(p2.port(), 7000);
|
|
|
|
let p2 = parse_port_or_addr(Some("hi there"), 1);
|
|
|
|
assert_eq!(p2.port(), 1);
|
|
|
|
let p3 = parse_port_or_addr(None, 1);
|
|
|
|
assert_eq!(p3.port(), 1);
|
2018-06-28 11:28:28 -07:00
|
|
|
}
|
|
|
|
}
|